From e64723d8ff9e6128e47c07883fdbcb7d6a80d07d Mon Sep 17 00:00:00 2001 From: Holger Schurig Date: Wed, 9 Mar 2005 15:05:47 +0000 Subject: some MNCI "Ramses" fixes/updates BKrev: 422f10cb2lTTtOfeHYxEiXthG3dCnQ --- .../mnci-combined.patch | 100262 ++++++++++++++++++ 1 file changed, 100262 insertions(+) (limited to 'packages/linux/mnci-ramses-2.4.21-rmk2-pxa1') diff --git a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch index e69de29bb2..fd1ba4db48 100644 --- a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch +++ b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch @@ -0,0 +1,100262 @@ + +# This is a cumulative patch consisting of: +# +# mtd-cvs.patch +# linux-vtcomparison.patch +# linux-mkdep.patch +# linux-iw241_we16-6.patch +# arm-noshortloads.patch +# pxa-pcmcia.patch +# pxa-smc91x.patch +# pxa-usb.patch +# pxa-usbeth.patch +# pxa-irda.patch +# pxa-ac97.patch +# pxa-timerint.patch +# fb-buffered.patch +# fb-turn180.patch +# i2c-ds1337.patch +# keyb-input.patch +# keyb-module.patch +# logo-noscrollregion.patch +# net-dhcp-timeout.patch +# pm.patch +# swap-performance.patch +# small-nocramdisk.patch +# smc91x-ethtool.patch +# ucb1x00.patch +# vmalloc.patch +# usb-sl811.patch +# orinoco013e.patch +# ramses.patch +# ramses-ac97.patch +# ramses-keyb.patch +# ramses-mtd.patch +# ramses-orinoco-ignorecis.patch +# ramses-pcmcia.patch +# ramses-serial.patch +# ramses-smc91x.patch +# ramses-sysctl.patch +# ramses-ucb1x00-dejitter.patch +# ramses-lcd.patch +# ramses-usb.patch +# ramses-corevolt.patch +# wedge.patch +# usb-sonycamera.patch +# ramses-ucb1x00-rotate.patch +# vt-noblank.patch +# +# Patch managed by http://www.holgerschurig.de/patcher.html +# + +--- /dev/null ++++ linux-2.4.21/Documentation/DocBook/librs.tmpl +@@ -0,0 +1,287 @@ ++ ++ ++ ++ ++ Reed-Solomon Library Programming Interface ++ ++ ++ ++ Thomas ++ Gleixner ++ ++
++ tglx@linutronix.de ++
++
++
++
++ ++ ++ 2004 ++ Thomas Gleixner ++ ++ ++ ++ ++ This documentation is free software; you can redistribute ++ it and/or modify it under the terms of the GNU General Public ++ License version 2 as published by the Free Software Foundation. ++ ++ ++ ++ This program is 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 ++ ++ ++ ++ For more details see the file COPYING in the source ++ distribution of Linux. ++ ++ ++
++ ++ ++ ++ ++ Introduction ++ ++ The generic Reed-Solomon Library provides encoding, decoding ++ and error correction functions. ++ ++ ++ Reed-Solomon codes are used in communication and storage ++ applications to ensure data integrity. ++ ++ ++ This documentation is provided for developers who want to utilize ++ the functions provided by the library. ++ ++ ++ ++ ++ Known Bugs And Assumptions ++ ++ None. ++ ++ ++ ++ ++ Usage ++ ++ This chapter provides examples how to use the library. ++ ++ ++ Initializing ++ ++ The init function init_rs returns a pointer to a ++ rs decoder structure, which holds the necessary ++ information for encoding, decoding and error correction ++ with the given polynomial. It either uses an existing ++ matching decoder or creates a new one. On creation all ++ the lookup tables for fast en/decoding are created. ++ The function may take a while, so make sure not to ++ call it in critical code paths. ++ ++ ++/* the Reed Solomon control structure */ ++static struct rs_control *rs_decoder; ++ ++/* Symbolsize is 10 (bits) ++ * Primitve polynomial is x^10+x^3+1 ++ * first consecutive root is 0 ++ * primitve element to generate roots = 1 ++ * generator polinomial degree (number of roots) = 6 ++ */ ++rs_decoder = init_rs (10, 0x409, 0, 1, 6); ++ ++ ++ ++ Encoding ++ ++ The encoder calculates the Reed-Solomon code over ++ the given data length and stores the result in ++ the parity buffer. Note that the parity buffer must ++ be initialized before calling the encoder. ++ ++ ++ The expanded data can be inverted on the fly by ++ providing a non zero inversion mask. The expanded data is ++ XOR'ed with the mask. This is used e.g. for FLASH ++ ECC, where the all 0xFF is inverted to an all 0x00. ++ The Reed-Solomon code for all 0x00 is all 0x00. The ++ code is inverted before storing to FLASH so it is 0xFF ++ too. This prevent's that reading from an erased FLASH ++ results in ECC errors. ++ ++ ++ The databytes are expanded to the given symbol size ++ on the fly. There is no support for encoding continuous ++ bitstreams with a symbol size != 8 at the moment. If ++ it is necessary it should be not a big deal to implement ++ such functionality. ++ ++ ++/* Parity buffer. Size = number of roots */ ++uint16_t par[6]; ++/* Initialize the parity buffer */ ++memset(par, 0, sizeof(par)); ++/* Encode 512 byte in data8. Store parity in buffer par */ ++encode_rs8 (rs_decoder, data8, 512, par, 0); ++ ++ ++ ++ Decoding ++ ++ The decoder calculates the syndrome over ++ the given data length and the received parity symbols ++ and corrects errors in the data. ++ ++ ++ If a syndrome is available from a hardware decoder ++ then the syndrome calculation is skipped. ++ ++ ++ The correction of the data buffer can be suppressed ++ by providing a correction pattern buffer and an error ++ location buffer to the decoder. The decoder stores the ++ calculated error location and the correction bitmask ++ in the given buffers. This is useful for hardware ++ decoders which use a weird bit ordering scheme. ++ ++ ++ The databytes are expanded to the given symbol size ++ on the fly. There is no support for decoding continuous ++ bitstreams with a symbolsize != 8 at the moment. If ++ it is necessary it should be not a big deal to implement ++ such functionality. ++ ++ ++ ++ ++ Decoding with syndrome calculation, direct data correction ++ ++ ++/* Parity buffer. Size = number of roots */ ++uint16_t par[6]; ++uint8_t data[512]; ++int numerr; ++/* Receive data */ ++..... ++/* Receive parity */ ++..... ++/* Decode 512 byte in data8.*/ ++numerr = decode_rs8 (rs_decoder, data8, par, 512, NULL, 0, NULL, 0, NULL); ++ ++ ++ ++ ++ ++ Decoding with syndrome given by hardware decoder, direct data correction ++ ++ ++/* Parity buffer. Size = number of roots */ ++uint16_t par[6], syn[6]; ++uint8_t data[512]; ++int numerr; ++/* Receive data */ ++..... ++/* Receive parity */ ++..... ++/* Get syndrome from hardware decoder */ ++..... ++/* Decode 512 byte in data8.*/ ++numerr = decode_rs8 (rs_decoder, data8, par, 512, syn, 0, NULL, 0, NULL); ++ ++ ++ ++ ++ ++ Decoding with syndrome given by hardware decoder, no direct data correction. ++ ++ ++ Note: It's not necessary to give data and received parity to the decoder. ++ ++ ++/* Parity buffer. Size = number of roots */ ++uint16_t par[6], syn[6], corr[8]; ++uint8_t data[512]; ++int numerr, errpos[8]; ++/* Receive data */ ++..... ++/* Receive parity */ ++..... ++/* Get syndrome from hardware decoder */ ++..... ++/* Decode 512 byte in data8.*/ ++numerr = decode_rs8 (rs_decoder, NULL, NULL, 512, syn, 0, errpos, 0, corr); ++for (i = 0; i < numerr; i++) { ++ do_error_correction_in_your_buffer(errpos[i], corr[i]); ++} ++ ++ ++ ++ ++ Cleanup ++ ++ The function free_rs frees the allocated resources, ++ if the caller is the last user of the decoder. ++ ++ ++/* Release resources */ ++free_rs(rs_decoder); ++ ++ ++ ++ ++ ++ ++ Structures ++ ++ This chapter contains the autogenerated documentation of the structures which are ++ used in the Reed-Solomon Library and are relevant for a developer. ++ ++!Iinclude/linux/rslib.h ++ ++ ++ ++ Public Functions Provided ++ ++ This chapter contains the autogenerated documentation of the Reed-Solomon functions ++ which are exported. ++ ++!Elib/reed_solomon/reed_solomon.c ++ ++ ++ ++ Credits ++ ++ The library code for encoding and decoding was written by Phil Karn. ++ ++ ++ Copyright 2002, Phil Karn, KA9Q ++ May be used under the terms of the GNU General Public License (GPL) ++ ++ ++ The wrapper functions and interfaces are written by Thomas Gleixner ++ ++ ++ Many users have provided bugfixes, improvements and helping hands for testing. ++ Thanks a lot. ++ ++ ++ The following people have contributed to this document: ++ ++ ++ Thomas Gleixnertglx@linutronix.de ++ ++ ++
+--- /dev/null ++++ linux-2.4.21/Documentation/DocBook/mtdnand.tmpl +@@ -0,0 +1,1318 @@ ++ ++ ++ ++ ++ MTD NAND Driver Programming Interface ++ ++ ++ ++ Thomas ++ Gleixner ++ ++
++ tglx@linutronix.de ++
++
++
++
++ ++ ++ 2004 ++ Thomas Gleixner ++ ++ ++ ++ ++ This documentation is free software; you can redistribute ++ it and/or modify it under the terms of the GNU General Public ++ License version 2 as published by the Free Software Foundation. ++ ++ ++ ++ This program is 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 ++ ++ ++ ++ For more details see the file COPYING in the source ++ distribution of Linux. ++ ++ ++
++ ++ ++ ++ ++ Introduction ++ ++ The generic NAND driver supports almost all NAND and AG-AND based ++ chips and connects them to the Memory Technology Devices (MTD) ++ subsystem of the Linux Kernel. ++ ++ ++ This documentation is provided for developers who want to implement ++ board drivers or filesystem drivers suitable for NAND devices. ++ ++ ++ ++ ++ Known Bugs And Assumptions ++ ++ None. ++ ++ ++ ++ ++ Documentation hints ++ ++ The function and structure docs are autogenerated. Each function and ++ struct member has a short description which is marked with an [XXX] identifier. ++ The following chapters explain the meaning of those identifiers. ++ ++ ++ Function identifiers [XXX] ++ ++ The functions are marked with [XXX] identifiers in the short ++ comment. The identifiers explain the usage and scope of the ++ functions. Following identifiers are used: ++ ++ ++ ++ [MTD Interface] ++ These functions provide the interface to the MTD kernel API. ++ They are not replacable and provide functionality ++ which is complete hardware independent. ++ ++ ++ [NAND Interface] ++ These functions are exported and provide the interface to the NAND kernel API. ++ ++ ++ [GENERIC] ++ Generic functions are not replacable and provide functionality ++ which is complete hardware independent. ++ ++ ++ [DEFAULT] ++ Default functions provide hardware related functionality which is suitable ++ for most of the implementations. These functions can be replaced by the ++ board driver if neccecary. Those functions are called via pointers in the ++ NAND chip description structure. The board driver can set the functions which ++ should be replaced by board dependend functions before calling nand_scan(). ++ If the function pointer is NULL on entry to nand_scan() then the pointer ++ is set to the default function which is suitable for the detected chip type. ++ ++ ++ ++ ++ Struct member identifiers [XXX] ++ ++ The struct members are marked with [XXX] identifiers in the ++ comment. The identifiers explain the usage and scope of the ++ members. Following identifiers are used: ++ ++ ++ ++ [INTERN] ++ These members are for NAND driver internal use only and must not be ++ modified. Most of these values are calculated from the chip geometry ++ information which is evaluated during nand_scan(). ++ ++ ++ [REPLACEABLE] ++ Replaceable members hold hardware related functions which can be ++ provided by the board driver. The board driver can set the functions which ++ should be replaced by board dependend functions before calling nand_scan(). ++ If the function pointer is NULL on entry to nand_scan() then the pointer ++ is set to the default function which is suitable for the detected chip type. ++ ++ ++ [BOARDSPECIFIC] ++ Board specific members hold hardware related information which must ++ be provided by the board driver. The board driver must set the function ++ pointers and datafields before calling nand_scan(). ++ ++ ++ [OPTIONAL] ++ Optional members can hold information relevant for the board driver. The ++ generic NAND driver code does not use this information. ++ ++ ++ ++ ++ ++ ++ Basic board driver ++ ++ For most boards it will be sufficient to provide just the ++ basic functions and fill out some really board dependend ++ members in the nand chip description structure. ++ See drivers/mtd/nand/skeleton for reference. ++ ++ ++ Basic defines ++ ++ At least you have to provide a mtd structure and ++ a storage for the ioremap'ed chip address. ++ You can allocate the mtd structure using kmalloc ++ or you can allocate it statically. ++ In case of static allocation you have to allocate ++ a nand_chip structure too. ++ ++ ++ Kmalloc based example ++ ++ ++static struct mtd_info *board_mtd; ++static unsigned long baseaddr; ++ ++ ++ Static example ++ ++ ++static struct mtd_info board_mtd; ++static struct nand_chip board_chip; ++static unsigned long baseaddr; ++ ++ ++ ++ Partition defines ++ ++ If you want to divide your device into parititions, then ++ enable the configuration switch CONFIG_MTD_PARITIONS and define ++ a paritioning scheme suitable to your board. ++ ++ ++#define NUM_PARTITIONS 2 ++static struct mtd_partition partition_info[] = { ++ { .name = "Flash partition 1", ++ .offset = 0, ++ .size = 8 * 1024 * 1024 }, ++ { .name = "Flash partition 2", ++ .offset = MTDPART_OFS_NEXT, ++ .size = MTDPART_SIZ_FULL }, ++}; ++ ++ ++ ++ Hardware control function ++ ++ The hardware control function provides access to the ++ control pins of the NAND chip(s). ++ The access can be done by GPIO pins or by address lines. ++ If you use address lines, make sure that the timing ++ requirements are met. ++ ++ ++ GPIO based example ++ ++ ++static void board_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ switch(cmd){ ++ case NAND_CTL_SETCLE: /* Set CLE pin high */ break; ++ case NAND_CTL_CLRCLE: /* Set CLE pin low */ break; ++ case NAND_CTL_SETALE: /* Set ALE pin high */ break; ++ case NAND_CTL_CLRALE: /* Set ALE pin low */ break; ++ case NAND_CTL_SETNCE: /* Set nCE pin low */ break; ++ case NAND_CTL_CLRNCE: /* Set nCE pin high */ break; ++ } ++} ++ ++ ++ Address lines based example. It's assumed that the ++ nCE pin is driven by a chip select decoder. ++ ++ ++static void board_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip *this = (struct nand_chip *) mtd->priv; ++ switch(cmd){ ++ case NAND_CTL_SETCLE: this->IO_ADDR_W |= CLE_ADRR_BIT; break; ++ case NAND_CTL_CLRCLE: this->IO_ADDR_W &= ~CLE_ADRR_BIT; break; ++ case NAND_CTL_SETALE: this->IO_ADDR_W |= ALE_ADRR_BIT; break; ++ case NAND_CTL_CLRALE: this->IO_ADDR_W &= ~ALE_ADRR_BIT; break; ++ } ++} ++ ++ ++ ++ Device ready function ++ ++ If the hardware interface has the ready busy pin of the NAND chip connected to a ++ GPIO or other accesible I/O pin, this function is used to read back the state of the ++ pin. The function has no arguments and should return 0, if the device is busy (R/B pin ++ is low) and 1, if the device is ready (R/B pin is high). ++ If the hardware interface does not give access to the ready busy pin, then ++ the function must not be defined and the function pointer this->dev_ready is set to NULL. ++ ++ ++ ++ Init function ++ ++ The init function allocates memory and sets up all the board ++ specific parameters and function pointers. When everything ++ is set up nand_scan() is called. This function tries to ++ detect and identify then chip. If a chip is found all the ++ internal data fields are initialized accordingly. ++ The structure(s) have to be zeroed out first and then filled with the neccecary ++ information about the device. ++ ++ ++int __init board_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); ++ if (!board_mtd) { ++ printk ("Unable to allocate NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Initialize structures */ ++ memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); ++ ++ /* map physical adress */ ++ baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); ++ if(!baseaddr){ ++ printk("Ioremap to access NAND chip failed\n"); ++ err = -EIO; ++ goto out_mtd; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (); ++ /* Link the private data with the MTD structure */ ++ board_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = baseaddr; ++ this->IO_ADDR_W = baseaddr; ++ /* Reference hardware control function */ ++ this->hwcontrol = board_hwcontrol; ++ /* Set command delay time, see datasheet for correct value */ ++ this->chip_delay = CHIP_DEPENDEND_COMMAND_DELAY; ++ /* Assign the device ready function, if available */ ++ this->dev_ready = board_dev_ready; ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (board_mtd, 1)) { ++ err = -ENXIO; ++ goto out_ior; ++ } ++ ++ add_mtd_partitions(board_mtd, partition_info, NUM_PARTITIONS); ++ goto out; ++ ++out_ior: ++ iounmap((void *)baseaddr); ++out_mtd: ++ kfree (board_mtd); ++out: ++ return err; ++} ++module_init(board_init); ++ ++ ++ ++ Exit function ++ ++ The exit function is only neccecary if the driver is ++ compiled as a module. It releases all resources which ++ are held by the chip driver and unregisters the partitions ++ in the MTD layer. ++ ++ ++#ifdef MODULE ++static void __exit board_cleanup (void) ++{ ++ /* Release resources, unregister device */ ++ nand_release (board_mtd); ++ ++ /* unmap physical adress */ ++ iounmap((void *)baseaddr); ++ ++ /* Free the MTD device structure */ ++ kfree (board_mtd); ++} ++module_exit(board_cleanup); ++#endif ++ ++ ++ ++ ++ ++ Advanced board driver functions ++ ++ This chapter describes the advanced functionality of the NAND ++ driver. For a list of functions which can be overridden by the board ++ driver see the documentation of the nand_chip structure. ++ ++ ++ Multiple chip control ++ ++ The nand driver can control chip arrays. Therefor the ++ board driver must provide an own select_chip function. This ++ function must (de)select the requested chip. ++ The function pointer in the nand_chip structure must ++ be set before calling nand_scan(). The maxchip parameter ++ of nand_scan() defines the maximum number of chips to ++ scan for. Make sure that the select_chip function can ++ handle the requested number of chips. ++ ++ ++ The nand driver concatenates the chips to one virtual ++ chip and provides this virtual chip to the MTD layer. ++ ++ ++ Note: The driver can only handle linear chip arrays ++ of equally sized chips. There is no support for ++ parallel arrays which extend the buswidth. ++ ++ ++ GPIO based example ++ ++ ++static void board_select_chip (struct mtd_info *mtd, int chip) ++{ ++ /* Deselect all chips, set all nCE pins high */ ++ GPIO(BOARD_NAND_NCE) |= 0xff; ++ if (chip >= 0) ++ GPIO(BOARD_NAND_NCE) &= ~ (1 << chip); ++} ++ ++ ++ Address lines based example. ++ Its assumed that the nCE pins are connected to an ++ address decoder. ++ ++ ++static void board_select_chip (struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = (struct nand_chip *) mtd->priv; ++ ++ /* Deselect all chips */ ++ this->IO_ADDR_R &= ~BOARD_NAND_ADDR_MASK; ++ this->IO_ADDR_W &= ~BOARD_NAND_ADDR_MASK; ++ switch (chip) { ++ case 0: ++ this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIP0; ++ this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIP0; ++ break; ++ .... ++ case n: ++ this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIPn; ++ this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIPn; ++ break; ++ } ++} ++ ++ ++ ++ Hardware ECC support ++ ++ Functions and constants ++ ++ The nand driver supports three different types of ++ hardware ECC. ++ ++ NAND_ECC_HW3_256 ++ Hardware ECC generator providing 3 bytes ECC per ++ 256 byte. ++ ++ NAND_ECC_HW3_512 ++ Hardware ECC generator providing 3 bytes ECC per ++ 512 byte. ++ ++ NAND_ECC_HW6_512 ++ Hardware ECC generator providing 6 bytes ECC per ++ 512 byte. ++ ++ NAND_ECC_HW8_512 ++ Hardware ECC generator providing 6 bytes ECC per ++ 512 byte. ++ ++ ++ If your hardware generator has a different functionality ++ add it at the appropriate place in nand_base.c ++ ++ ++ The board driver must provide following functions: ++ ++ enable_hwecc ++ This function is called before reading / writing to ++ the chip. Reset or initialize the hardware generator ++ in this function. The function is called with an ++ argument which let you distinguish between read ++ and write operations. ++ ++ calculate_ecc ++ This function is called after read / write from / to ++ the chip. Transfer the ECC from the hardware to ++ the buffer. If the option NAND_HWECC_SYNDROME is set ++ then the function is only called on write. See below. ++ ++ correct_data ++ In case of an ECC error this function is called for ++ error detection and correction. Return 1 respectively 2 ++ in case the error can be corrected. If the error is ++ not correctable return -1. If your hardware generator ++ matches the default algorithm of the nand_ecc software ++ generator then use the correction function provided ++ by nand_ecc instead of implementing duplicated code. ++ ++ ++ ++ ++ ++ Hardware ECC with syndrome calculation ++ ++ Many hardware ECC implementations provide Reed-Solomon ++ codes and calculate an error syndrome on read. The syndrome ++ must be converted to a standard Reed-Solomon syndrome ++ before calling the error correction code in the generic ++ Reed-Solomon library. ++ ++ ++ The ECC bytes must be placed immidiately after the data ++ bytes in order to make the syndrome generator work. This ++ is contrary to the usual layout used by software ECC. The ++ seperation of data and out of band area is not longer ++ possible. The nand driver code handles this layout and ++ the remaining free bytes in the oob area are managed by ++ the autoplacement code. Provide a matching oob-layout ++ in this case. See rts_from4.c and diskonchip.c for ++ implementation reference. In those cases we must also ++ use bad block tables on FLASH, because the ECC layout is ++ interferring with the bad block marker positions. ++ See bad block table support for details. ++ ++ ++ ++ ++ Bad block table support ++ ++ Most NAND chips mark the bad blocks at a defined ++ position in the spare area. Those blocks must ++ not be erased under any circumstances as the bad ++ block information would be lost. ++ It is possible to check the bad block mark each ++ time when the blocks are accessed by reading the ++ spare area of the first page in the block. This ++ is time consuming so a bad block table is used. ++ ++ ++ The nand driver supports various types of bad block ++ tables. ++ ++ Per device ++ The bad block table contains all bad block information ++ of the device which can consist of multiple chips. ++ ++ Per chip ++ A bad block table is used per chip and contains the ++ bad block information for this particular chip. ++ ++ Fixed offset ++ The bad block table is located at a fixed offset ++ in the chip (device). This applies to various ++ DiskOnChip devices. ++ ++ Automatic placed ++ The bad block table is automatically placed and ++ detected either at the end or at the beginning ++ of a chip (device) ++ ++ Mirrored tables ++ The bad block table is mirrored on the chip (device) to ++ allow updates of the bad block table without data loss. ++ ++ ++ ++ ++ nand_scan() calls the function nand_default_bbt(). ++ nand_default_bbt() selects appropriate default ++ bad block table desriptors depending on the chip information ++ which was retrieved by nand_scan(). ++ ++ ++ The standard policy is scanning the device for bad ++ blocks and build a ram based bad block table which ++ allows faster access than always checking the ++ bad block information on the flash chip itself. ++ ++ ++ Flash based tables ++ ++ It may be desired or neccecary to keep a bad block table in FLASH. ++ For AG-AND chips this is mandatory, as they have no factory marked ++ bad blocks. They have factory marked good blocks. The marker pattern ++ is erased when the block is erased to be reused. So in case of ++ powerloss before writing the pattern back to the chip this block ++ would be lost and added to the bad blocks. Therefor we scan the ++ chip(s) when we detect them the first time for good blocks and ++ store this information in a bad block table before erasing any ++ of the blocks. ++ ++ ++ The blocks in which the tables are stored are procteted against ++ accidental access by marking them bad in the memory bad block ++ table. The bad block table managment functions are allowed ++ to circumvernt this protection. ++ ++ ++ The simplest way to activate the FLASH based bad block table support ++ is to set the option NAND_USE_FLASH_BBT in the option field of ++ the nand chip structure before calling nand_scan(). For AG-AND ++ chips is this done by default. ++ This activates the default FLASH based bad block table functionality ++ of the NAND driver. The default bad block table options are ++ ++ Store bad block table per chip ++ Use 2 bits per block ++ Automatic placement at the end of the chip ++ Use mirrored tables with version numbers ++ Reserve 4 blocks at the end of the chip ++ ++ ++ ++ ++ User defined tables ++ ++ User defined tables are created by filling out a ++ nand_bbt_descr structure and storing the pointer in the ++ nand_chip structure member bbt_td before calling nand_scan(). ++ If a mirror table is neccecary a second structure must be ++ created and a pointer to this structure must be stored ++ in bbt_md inside the nand_chip structure. If the bbt_md ++ member is set to NULL then only the main table is used ++ and no scan for the mirrored table is performed. ++ ++ ++ The most important field in the nand_bbt_descr structure ++ is the options field. The options define most of the ++ table properties. Use the predefined constants from ++ nand.h to define the options. ++ ++ Number of bits per block ++ The supported number of bits is 1, 2, 4, 8. ++ Table per chip ++ Setting the constant NAND_BBT_PERCHIP selects that ++ a bad block table is managed for each chip in a chip array. ++ If this option is not set then a per device bad block table ++ is used. ++ Table location is absolute ++ Use the option constant NAND_BBT_ABSPAGE and ++ define the absolute page number where the bad block ++ table starts in the field pages. If you have selected bad block ++ tables per chip and you have a multi chip array then the start page ++ must be given for each chip in the chip array. Note: there is no scan ++ for a table ident pattern performed, so the fields ++ pattern, veroffs, offs, len can be left uninitialized ++ Table location is automatically detected ++ The table can either be located in the first or the last good ++ blocks of the chip (device). Set NAND_BBT_LASTBLOCK to place ++ the bad block table at the end of the chip (device). The ++ bad block tables are marked and identified by a pattern which ++ is stored in the spare area of the first page in the block which ++ holds the bad block table. Store a pointer to the pattern ++ in the pattern field. Further the length of the pattern has to be ++ stored in len and the offset in the spare area must be given ++ in the offs member of the nand_bbt_descr stucture. For mirrored ++ bad block tables different patterns are mandatory. ++ Table creation ++ Set the option NAND_BBT_CREATE to enable the table creation ++ if no table can be found during the scan. Usually this is done only ++ once if a new chip is found. ++ Table write support ++ Set the option NAND_BBT_WRITE to enable the table write support. ++ This allows the update of the bad block table(s) in case a block has ++ to be marked bad due to wear. The MTD interface function block_markbad ++ is calling the update function of the bad block table. If the write ++ support is enabled then the table is updated on FLASH. ++ ++ Note: Write support should only be enabled for mirrored tables with ++ version control. ++ ++ Table version control ++ Set the option NAND_BBT_VERSION to enable the table version control. ++ It's highly recommended to enable this for mirrored tables with write ++ support. It makes sure that the risk of loosing the bad block ++ table information is reduced to the loss of the information about the ++ one worn out block which should be marked bad. The version is stored in ++ 4 consecutive bytes in the spare area of the device. The position of ++ the version number is defined by the member veroffs in the bad block table ++ descriptor. ++ Save block contents on write ++ ++ In case that the block which holds the bad block table does contain ++ other useful information, set the option NAND_BBT_SAVECONTENT. When ++ the bad block table is written then the whole block is read the bad ++ block table is updated and the block is erased and everything is ++ written back. If this option is not set only the bad block table ++ is written and everything else in the block is ignored and erased. ++ ++ Number of reserved blocks ++ ++ For automatic placement some blocks must be reserved for ++ bad block table storage. The number of reserved blocks is defined ++ in the maxblocks member of the babd block table description structure. ++ Reserving 4 blocks for mirrored tables should be a reasonable number. ++ This also limits the number of blocks which are scanned for the bad ++ block table ident pattern. ++ ++ ++ ++ ++ ++ ++ Spare area (auto)placement ++ ++ The nand driver implements different possibilities for ++ placement of filesystem data in the spare area, ++ ++ Placement defined by fs driver ++ Automatic placement ++ ++ The default placement function is automatic placement. The ++ nand driver has built in default placement schemes for the ++ various chiptypes. If due to hardware ECC functionality the ++ default placement does not fit then the board driver can ++ provide a own placement scheme. ++ ++ ++ File system drivers can provide a own placement scheme which ++ is used instead of the default placement scheme. ++ ++ ++ Placement schemes are defined by a nand_oobinfo structure ++ ++struct nand_oobinfo { ++ int useecc; ++ int eccbytes; ++ int eccpos[24]; ++ int oobfree[8][2]; ++}; ++ ++ ++ useecc ++ The useecc member controls the ecc and placement function. The header ++ file include/mtd/mtd-abi.h contains constants to select ecc and ++ placement. MTD_NANDECC_OFF switches off the ecc complete. This is ++ not recommended and available for testing and diagnosis only. ++ MTD_NANDECC_PLACE selects caller defined placement, MTD_NANDECC_AUTOPLACE ++ selects automatic placement. ++ ++ eccbytes ++ The eccbytes member defines the number of ecc bytes per page. ++ ++ eccpos ++ The eccpos array holds the byte offsets in the spare area where ++ the ecc codes are placed. ++ ++ oobfree ++ The oobfree array defines the areas in the spare area which can be ++ used for automatic placement. The information is given in the format ++ {offset, size}. offset defines the start of the usable area, size the ++ length in bytes. More than one area can be defined. The list is terminated ++ by an {0, 0} entry. ++ ++ ++ ++ ++ Placement defined by fs driver ++ ++ The calling function provides a pointer to a nand_oobinfo ++ structure which defines the ecc placement. For writes the ++ caller must provide a spare area buffer along with the ++ data buffer. The spare area buffer size is (number of pages) * ++ (size of spare area). For reads the buffer size is ++ (number of pages) * ((size of spare area) + (number of ecc ++ steps per page) * sizeof (int)). The driver stores the ++ result of the ecc check for each tuple in the spare buffer. ++ The storage sequence is ++ ++ ++ <spare data page 0><ecc result 0>...<ecc result n> ++ ++ ++ ... ++ ++ ++ <spare data page n><ecc result 0>...<ecc result n> ++ ++ ++ This is a legacy mode used by YAFFS1. ++ ++ ++ If the spare area buffer is NULL then only the ECC placement is ++ done according to the given scheme in the nand_oobinfo structure. ++ ++ ++ ++ Automatic placement ++ ++ Automatic placement uses the built in defaults to place the ++ ecc bytes in the spare area. If filesystem data have to be stored / ++ read into the spare area then the calling function must provide a ++ buffer. The buffer size per page is determined by the oobfree array in ++ the nand_oobinfo structure. ++ ++ ++ If the spare area buffer is NULL then only the ECC placement is ++ done according to the default builtin scheme. ++ ++ ++ ++ User space placement selection ++ ++ All non ecc functions like mtd->read and mtd->write use an internal ++ structure, which can be set by an ioctl. This structure is preset ++ to the autoplacement default. ++ ++ ioctl (fd, MEMSETOOBSEL, oobsel); ++ ++ oobsel is a pointer to a user supplied structure of type ++ nand_oobconfig. The contents of this structure must match the ++ criteria of the filesystem, which will be used. See an example in utils/nandwrite.c. ++ ++ ++ ++ ++ Spare area autoplacement default schemes ++ ++ 256 byte pagesize ++ ++ ++Offset ++Content ++Comment ++ ++ ++0x00 ++ECC byte 0 ++Error correction code byte 0 ++ ++ ++0x01 ++ECC byte 1 ++Error correction code byte 1 ++ ++ ++0x02 ++ECC byte 2 ++Error correction code byte 2 ++ ++ ++0x03 ++Autoplace 0 ++ ++ ++ ++0x04 ++Autoplace 1 ++ ++ ++ ++0x05 ++Bad block marker ++If any bit in this byte is zero, then this block is bad. ++This applies only to the first page in a block. In the remaining ++pages this byte is reserved ++ ++ ++0x06 ++Autoplace 2 ++ ++ ++ ++0x07 ++Autoplace 3 ++ ++ ++ ++ ++ ++ 512 byte pagesize ++ ++ ++Offset ++Content ++Comment ++ ++ ++0x00 ++ECC byte 0 ++Error correction code byte 0 of the lower 256 Byte data in ++this page ++ ++ ++0x01 ++ECC byte 1 ++Error correction code byte 1 of the lower 256 Bytes of data ++in this page ++ ++ ++0x02 ++ECC byte 2 ++Error correction code byte 2 of the lower 256 Bytes of data ++in this page ++ ++ ++0x03 ++ECC byte 3 ++Error correction code byte 0 of the upper 256 Bytes of data ++in this page ++ ++ ++0x04 ++reserved ++reserved ++ ++ ++0x05 ++Bad block marker ++If any bit in this byte is zero, then this block is bad. ++This applies only to the first page in a block. In the remaining ++pages this byte is reserved ++ ++ ++0x06 ++ECC byte 4 ++Error correction code byte 1 of the upper 256 Bytes of data ++in this page ++ ++ ++0x07 ++ECC byte 5 ++Error correction code byte 2 of the upper 256 Bytes of data ++in this page ++ ++ ++0x08 - 0x0F ++Autoplace 0 - 7 ++ ++ ++ ++ ++ ++ 2048 byte pagesize ++ ++ ++Offset ++Content ++Comment ++ ++ ++0x00 ++Bad block marker ++If any bit in this byte is zero, then this block is bad. ++This applies only to the first page in a block. In the remaining ++pages this byte is reserved ++ ++ ++0x01 ++Reserved ++Reserved ++ ++ ++0x02-0x27 ++Autoplace 0 - 37 ++ ++ ++ ++0x28 ++ECC byte 0 ++Error correction code byte 0 of the first 256 Byte data in ++this page ++ ++ ++0x29 ++ECC byte 1 ++Error correction code byte 1 of the first 256 Bytes of data ++in this page ++ ++ ++0x2A ++ECC byte 2 ++Error correction code byte 2 of the first 256 Bytes data in ++this page ++ ++ ++0x2B ++ECC byte 3 ++Error correction code byte 0 of the second 256 Bytes of data ++in this page ++ ++ ++0x2C ++ECC byte 4 ++Error correction code byte 1 of the second 256 Bytes of data ++in this page ++ ++ ++0x2D ++ECC byte 5 ++Error correction code byte 2 of the second 256 Bytes of data ++in this page ++ ++ ++0x2E ++ECC byte 6 ++Error correction code byte 0 of the third 256 Bytes of data ++in this page ++ ++ ++0x2F ++ECC byte 7 ++Error correction code byte 1 of the third 256 Bytes of data ++in this page ++ ++ ++0x30 ++ECC byte 8 ++Error correction code byte 2 of the third 256 Bytes of data ++in this page ++ ++ ++0x31 ++ECC byte 9 ++Error correction code byte 0 of the fourth 256 Bytes of data ++in this page ++ ++ ++0x32 ++ECC byte 10 ++Error correction code byte 1 of the fourth 256 Bytes of data ++in this page ++ ++ ++0x33 ++ECC byte 11 ++Error correction code byte 2 of the fourth 256 Bytes of data ++in this page ++ ++ ++0x34 ++ECC byte 12 ++Error correction code byte 0 of the fifth 256 Bytes of data ++in this page ++ ++ ++0x35 ++ECC byte 13 ++Error correction code byte 1 of the fifth 256 Bytes of data ++in this page ++ ++ ++0x36 ++ECC byte 14 ++Error correction code byte 2 of the fifth 256 Bytes of data ++in this page ++ ++ ++0x37 ++ECC byte 15 ++Error correction code byte 0 of the sixt 256 Bytes of data ++in this page ++ ++ ++0x38 ++ECC byte 16 ++Error correction code byte 1 of the sixt 256 Bytes of data ++in this page ++ ++ ++0x39 ++ECC byte 17 ++Error correction code byte 2 of the sixt 256 Bytes of data ++in this page ++ ++ ++0x3A ++ECC byte 18 ++Error correction code byte 0 of the seventh 256 Bytes of ++data in this page ++ ++ ++0x3B ++ECC byte 19 ++Error correction code byte 1 of the seventh 256 Bytes of ++data in this page ++ ++ ++0x3C ++ECC byte 20 ++Error correction code byte 2 of the seventh 256 Bytes of ++data in this page ++ ++ ++0x3D ++ECC byte 21 ++Error correction code byte 0 of the eigth 256 Bytes of data ++in this page ++ ++ ++0x3E ++ECC byte 22 ++Error correction code byte 1 of the eigth 256 Bytes of data ++in this page ++ ++ ++0x3F ++ECC byte 23 ++Error correction code byte 2 of the eigth 256 Bytes of data ++in this page ++ ++ ++ ++ ++ ++ ++ ++ Filesystem support ++ ++ The NAND driver provides all neccecary functions for a ++ filesystem via the MTD interface. ++ ++ ++ Filesystems must be aware of the NAND pecularities and ++ restrictions. One major restrictions of NAND Flash is, that you cannot ++ write as often as you want to a page. The consecutive writes to a page, ++ before erasing it again, are restricted to 1-3 writes, depending on the ++ manufacturers specifications. This applies similar to the spare area. ++ ++ ++ Therefor NAND aware filesystems must either write in page size chunks ++ or hold a writebuffer to collect smaller writes until they sum up to ++ pagesize. Available NAND aware filesystems: JFFS2, YAFFS. ++ ++ ++ The spare area usage to store filesystem data is controlled by ++ the spare area placement functionality which is described in one ++ of the earlier chapters. ++ ++ ++ ++ Tools ++ ++ The MTD project provides a couple of helpful tools to handle NAND Flash. ++ ++ flasherase, flasheraseall: Erase and format FLASH partitions ++ nandwrite: write filesystem images to NAND FLASH ++ nanddump: dump the contents of a NAND FLASH partitions ++ ++ ++ ++ These tools are aware of the NAND restrictions. Please use those tools ++ instead of complaining about errors which are caused by non NAND aware ++ access methods. ++ ++ ++ ++ ++ Constants ++ ++ This chapter describes the constants which might be relevant for a driver developer. ++ ++ ++ Chip option constants ++ ++ Constants for chip id table ++ ++ These constants are defined in nand.h. They are ored together to describe ++ the chip functionality. ++ ++/* Chip can not auto increment pages */ ++#define NAND_NO_AUTOINCR 0x00000001 ++/* Buswitdh is 16 bit */ ++#define NAND_BUSWIDTH_16 0x00000002 ++/* Device supports partial programming without padding */ ++#define NAND_NO_PADDING 0x00000004 ++/* Chip has cache program function */ ++#define NAND_CACHEPRG 0x00000008 ++/* Chip has copy back function */ ++#define NAND_COPYBACK 0x00000010 ++/* AND Chip which has 4 banks and a confusing page / block ++ * assignment. See Renesas datasheet for further information */ ++#define NAND_IS_AND 0x00000020 ++/* Chip has a array of 4 pages which can be read without ++ * additional ready /busy waits */ ++#define NAND_4PAGE_ARRAY 0x00000040 ++ ++ ++ ++ ++ Constants for runtime options ++ ++ These constants are defined in nand.h. They are ored together to describe ++ the functionality. ++ ++/* Use a flash based bad block table. This option is parsed by the ++ * default bad block table function (nand_default_bbt). */ ++#define NAND_USE_FLASH_BBT 0x00010000 ++/* The hw ecc generator provides a syndrome instead a ecc value on read ++ * This can only work if we have the ecc bytes directly behind the ++ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ ++#define NAND_HWECC_SYNDROME 0x00020000 ++ ++ ++ ++ ++ ++ ++ ECC selection constants ++ ++ Use these constants to select the ECC algorithm. ++ ++/* No ECC. Usage is not recommended ! */ ++#define NAND_ECC_NONE 0 ++/* Software ECC 3 byte ECC per 256 Byte data */ ++#define NAND_ECC_SOFT 1 ++/* Hardware ECC 3 byte ECC per 256 Byte data */ ++#define NAND_ECC_HW3_256 2 ++/* Hardware ECC 3 byte ECC per 512 Byte data */ ++#define NAND_ECC_HW3_512 3 ++/* Hardware ECC 6 byte ECC per 512 Byte data */ ++#define NAND_ECC_HW6_512 4 ++/* Hardware ECC 6 byte ECC per 512 Byte data */ ++#define NAND_ECC_HW8_512 6 ++ ++ ++ ++ ++ ++ Hardware control related constants ++ ++ These constants describe the requested hardware access function when ++ the boardspecific hardware control function is called ++ ++/* Select the chip by setting nCE to low */ ++#define NAND_CTL_SETNCE 1 ++/* Deselect the chip by setting nCE to high */ ++#define NAND_CTL_CLRNCE 2 ++/* Select the command latch by setting CLE to high */ ++#define NAND_CTL_SETCLE 3 ++/* Deselect the command latch by setting CLE to low */ ++#define NAND_CTL_CLRCLE 4 ++/* Select the address latch by setting ALE to high */ ++#define NAND_CTL_SETALE 5 ++/* Deselect the address latch by setting ALE to low */ ++#define NAND_CTL_CLRALE 6 ++/* Set write protection by setting WP to high. Not used! */ ++#define NAND_CTL_SETWP 7 ++/* Clear write protection by setting WP to low. Not used! */ ++#define NAND_CTL_CLRWP 8 ++ ++ ++ ++ ++ ++ Bad block table related constants ++ ++ These constants describe the options used for bad block ++ table descriptors. ++ ++/* Options for the bad block table descriptors */ ++ ++/* The number of bits used per block in the bbt on the device */ ++#define NAND_BBT_NRBITS_MSK 0x0000000F ++#define NAND_BBT_1BIT 0x00000001 ++#define NAND_BBT_2BIT 0x00000002 ++#define NAND_BBT_4BIT 0x00000004 ++#define NAND_BBT_8BIT 0x00000008 ++/* The bad block table is in the last good block of the device */ ++#define NAND_BBT_LASTBLOCK 0x00000010 ++/* The bbt is at the given page, else we must scan for the bbt */ ++#define NAND_BBT_ABSPAGE 0x00000020 ++/* The bbt is at the given page, else we must scan for the bbt */ ++#define NAND_BBT_SEARCH 0x00000040 ++/* bbt is stored per chip on multichip devices */ ++#define NAND_BBT_PERCHIP 0x00000080 ++/* bbt has a version counter at offset veroffs */ ++#define NAND_BBT_VERSION 0x00000100 ++/* Create a bbt if none axists */ ++#define NAND_BBT_CREATE 0x00000200 ++/* Search good / bad pattern through all pages of a block */ ++#define NAND_BBT_SCANALLPAGES 0x00000400 ++/* Scan block empty during good / bad block scan */ ++#define NAND_BBT_SCANEMPTY 0x00000800 ++/* Write bbt if neccecary */ ++#define NAND_BBT_WRITE 0x00001000 ++/* Read and write back block contents when writing bbt */ ++#define NAND_BBT_SAVECONTENT 0x00002000 ++ ++ ++ ++ ++ ++ ++ ++ Structures ++ ++ This chapter contains the autogenerated documentation of the structures which are ++ used in the NAND driver and might be relevant for a driver developer. Each ++ struct member has a short description which is marked with an [XXX] identifier. ++ See the chapter "Documentation hints" for an explanation. ++ ++!Iinclude/linux/mtd/nand.h ++ ++ ++ ++ Public Functions Provided ++ ++ This chapter contains the autogenerated documentation of the NAND kernel API functions ++ which are exported. Each function has a short description which is marked with an [XXX] identifier. ++ See the chapter "Documentation hints" for an explanation. ++ ++!Edrivers/mtd/nand/nand_base.c ++!Edrivers/mtd/nand/nand_bbt.c ++!Edrivers/mtd/nand/nand_ecc.c ++ ++ ++ ++ Internal Functions Provided ++ ++ This chapter contains the autogenerated documentation of the NAND driver internal functions. ++ Each function has a short description which is marked with an [XXX] identifier. ++ See the chapter "Documentation hints" for an explanation. ++ The functions marked with [DEFAULT] might be relevant for a board driver developer. ++ ++!Idrivers/mtd/nand/nand_base.c ++!Idrivers/mtd/nand/nand_bbt.c ++!Idrivers/mtd/nand/nand_ecc.c ++ ++ ++ ++ Credits ++ ++ The following people have contributed to the NAND driver: ++ ++ Steven J. Hillsjhill@realitydiluted.com ++ David Woodhousedwmw2@infradead.org ++ Thomas Gleixnertglx@linutronix.de ++ ++ A lot of users have provided bugfixes, improvements and helping hands for testing. ++ Thanks a lot. ++ ++ ++ The following people have contributed to this document: ++ ++ Thomas Gleixnertglx@linutronix.de ++ ++ ++ ++
+--- linux-2.4.21/Makefile~linux-mkdep ++++ linux-2.4.21/Makefile +@@ -14,10 +14,11 @@ + else echo sh; fi ; fi) + TOPDIR := $(shell /bin/pwd) + ++PATH := /usr/local/arm/3.3/bin:$(PATH) + HPATH = $(TOPDIR)/include + FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net $(HPATH)/math-emu + +-HOSTCC = gcc ++HOSTCC = ccache gcc + HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer + + CROSS_COMPILE = arm-linux- +@@ -28,7 +29,7 @@ + + AS = $(CROSS_COMPILE)as + LD = $(CROSS_COMPILE)ld +-CC = $(CROSS_COMPILE)gcc ++CC = ccache $(CROSS_COMPILE)gcc + CPP = $(CC) -E + AR = $(CROSS_COMPILE)ar + NM = $(CROSS_COMPILE)nm +@@ -80,6 +81,7 @@ + # makefile but the arguement can be passed to make if needed. + # + ++export INSTALL_MOD_PATH = /tftpboot/ramses2 + MODLIB := $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) + export MODLIB + +@@ -140,7 +142,6 @@ + DRIVERS-y += drivers/serial/serial.o \ + drivers/char/char.o \ + drivers/block/block.o \ +- drivers/misc/misc.o \ + drivers/net/net.o + DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o + DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o +@@ -197,6 +198,7 @@ + DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o + DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o + DRIVERS-$(CONFIG_ARCH_AT91RM9200) += drivers/at91/at91drv.o ++DRIVERS-y += drivers/misc/misc.o + + DRIVERS := $(DRIVERS-y) + +@@ -416,7 +418,7 @@ + endif + .PHONY: _modinst_post + _modinst_post: _modinst_post_pcmcia +- if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi ++# if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi + + # Backwards compatibilty symlinks for people still using old versions + # of pcmcia-cs with hard coded pathnames on insmod. Remove +@@ -495,7 +497,7 @@ + ifdef CONFIG_MODVERSIONS + $(MAKE) update-modverfile + endif +- scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend ++ $(foreach, dir, $(FINDHPATH), scripts/mkdep -- `find $(dir) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` >> .hdepend) + scripts/mkdep -- init/*.c > .depend + + ifdef CONFIG_MODVERSIONS +@@ -574,3 +576,14 @@ + . scripts/mkversion > .version ; \ + rpm -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ; \ + rm $(TOPDIR)/../$(KERNELPATH).tar.gz ++ ++ ++ ++# ++# Burn Linux Image for ArmBoot using the bdi2000 ++# ++burn burn_zImage: ++ ../hwtester/burner.py arch/arm/boot/zImage 0x40000 ++ ++publish: arch/arm/boot/zImage ++ cp arch/arm/boot/zImage /tftpboot/bdi/zImage.testing +--- linux-2.4.21/arch/arm/Makefile~arm-noshortloads ++++ linux-2.4.21/arch/arm/Makefile +@@ -55,8 +55,8 @@ + #tune-$(CONFIG_CPU_XSCALE) :=-mtune=xscale + tune-$(CONFIG_CPU_XSCALE) :=-mtune=strongarm + +-CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm +-CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm ++CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm ++CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm + AFLAGS +=$(apcs-y) $(arch-y) -msoft-float + + ifeq ($(CONFIG_CPU_26),y) +@@ -289,7 +289,7 @@ + arch/arm/kernel arch/arm/mm arch/arm/lib: dummy + $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@) + +-bzImage zImage zinstall Image xipImage bootpImage install: vmlinux ++bzImage zImage zinstall Image xipImage bootpImage: vmlinux + @$(MAKEBOOT) $@ + + CLEAN_FILES += \ +--- linux-2.4.21/arch/arm/config.in~pm ++++ linux-2.4.21/arch/arm/config.in +@@ -152,6 +152,7 @@ + dep_bool ' Intel DBPXA250 Development Platform' CONFIG_ARCH_LUBBOCK $CONFIG_ARCH_PXA + dep_bool ' Accelent Xscale IDP' CONFIG_ARCH_PXA_IDP $CONFIG_ARCH_PXA + dep_bool ' Intrinsyc CerfBoard' CONFIG_ARCH_PXA_CERF $CONFIG_ARCH_PXA ++dep_bool ' M und N Ramses' CONFIG_ARCH_RAMSES $CONFIG_ARCH_PXA + dep_bool ' Trizeps-II MT6N' CONFIG_ARCH_TRIZEPS2 $CONFIG_ARCH_PXA + + if [ "$CONFIG_ARCH_PXA_CERF" = "y" ]; then +@@ -586,6 +587,7 @@ + tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + dep_bool 'Power Management support (experimental)' CONFIG_PM $CONFIG_EXPERIMENTAL ++dep_bool 'Advanced power management emulation support' CONFIG_APM $CONFIG_PM + dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32 + string 'Default kernel command string' CONFIG_CMDLINE "" + +--- /dev/null ++++ linux-2.4.21/arch/arm/def-configs/ramses +@@ -0,0 +1,1152 @@ ++# ++# Automatically generated by make menuconfig: don't edit ++# ++CONFIG_ARM=y ++# CONFIG_EISA is not set ++# CONFIG_SBUS is not set ++# CONFIG_MCA is not set ++CONFIG_UID16=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set ++# CONFIG_GENERIC_BUST_SPINLOCK is not set ++# CONFIG_GENERIC_ISA_DMA is not set ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++# CONFIG_OBSOLETE is not set ++ ++# ++# Loadable module support ++# ++CONFIG_MODULES=y ++# CONFIG_MODVERSIONS is not set ++CONFIG_KMOD=y ++ ++# ++# System Type ++# ++# CONFIG_ARCH_ANAKIN is not set ++# CONFIG_ARCH_ARCA5K is not set ++# CONFIG_ARCH_CLPS7500 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CO285 is not set ++CONFIG_ARCH_PXA=y ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_CAMELOT is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_OMAHA is not set ++# CONFIG_ARCH_L7200 is not set ++# CONFIG_ARCH_MX1ADS is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_RISCSTATION is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_AT91RM9200 is not set ++ ++# ++# Archimedes/A5000 Implementations ++# ++# CONFIG_ARCH_ARC is not set ++# CONFIG_ARCH_A5K is not set ++ ++# ++# Footbridge Implementations ++# ++# CONFIG_ARCH_CATS is not set ++# CONFIG_ARCH_PERSONAL_SERVER is not set ++# CONFIG_ARCH_EBSA285_ADDIN is not set ++# CONFIG_ARCH_EBSA285_HOST is not set ++# CONFIG_ARCH_NETWINDER is not set ++ ++# ++# SA11x0 Implementations ++# ++# CONFIG_SA1100_ACCELENT is not set ++# CONFIG_SA1100_ASSABET is not set ++# CONFIG_ASSABET_NEPONSET is not set ++# CONFIG_SA1100_ADSAGC is not set ++# CONFIG_SA1100_ADSBITSY is not set ++# CONFIG_SA1100_ADSBITSYPLUS is not set ++# CONFIG_SA1100_BRUTUS is not set ++# CONFIG_SA1100_CEP is not set ++# CONFIG_SA1100_CERF is not set ++# CONFIG_SA1100_H3100 is not set ++# CONFIG_SA1100_H3600 is not set ++# CONFIG_SA1100_H3800 is not set ++# CONFIG_SA1100_H3XXX is not set ++# CONFIG_H3600_SLEEVE is not set ++# CONFIG_SA1100_EXTENEX1 is not set ++# CONFIG_SA1100_FLEXANET is not set ++# CONFIG_SA1100_FREEBIRD is not set ++# CONFIG_SA1100_FRODO is not set ++# CONFIG_SA1100_GRAPHICSCLIENT is not set ++# CONFIG_SA1100_GRAPHICSMASTER is not set ++# CONFIG_SA1100_HACKKIT is not set ++# CONFIG_SA1100_BADGE4 is not set ++# CONFIG_SA1100_JORNADA720 is not set ++# CONFIG_SA1100_HUW_WEBPANEL is not set ++# CONFIG_SA1100_ITSY is not set ++# CONFIG_SA1100_LART is not set ++# CONFIG_SA1100_NANOENGINE is not set ++# CONFIG_SA1100_OMNIMETER is not set ++# CONFIG_SA1100_PANGOLIN is not set ++# CONFIG_SA1100_PLEB is not set ++# CONFIG_SA1100_PT_SYSTEM3 is not set ++# CONFIG_SA1100_SHANNON is not set ++# CONFIG_SA1100_SHERMAN is not set ++# CONFIG_SA1100_SIMPAD is not set ++# CONFIG_SA1100_SIMPUTER is not set ++# CONFIG_SA1100_PFS168 is not set ++# CONFIG_SA1100_VICTOR is not set ++# CONFIG_SA1100_XP860 is not set ++# CONFIG_SA1100_YOPY is not set ++# CONFIG_SA1100_USB is not set ++# CONFIG_SA1100_USB_NETLINK is not set ++# CONFIG_SA1100_USB_CHAR is not set ++# CONFIG_SA1100_SSP is not set ++ ++# ++# AT91RM9200 Implementations ++# ++# CONFIG_ARCH_AT91RM9200DK is not set ++ ++# ++# Intel PXA250/210 Implementations ++# ++# CONFIG_ARCH_LUBBOCK is not set ++# CONFIG_ARCH_PXA_IDP is not set ++# CONFIG_ARCH_PXA_CERF is not set ++CONFIG_ARCH_RAMSES=y ++# CONFIG_ARCH_TRIZEPS2 is not set ++CONFIG_PXA_USB=m ++CONFIG_PXA_USB_NETLINK=m ++CONFIG_PXA_USB_CHAR=m ++ ++# ++# CLPS711X/EP721X Implementations ++# ++# CONFIG_ARCH_AUTCPU12 is not set ++# CONFIG_ARCH_CDB89712 is not set ++# CONFIG_ARCH_CLEP7312 is not set ++# CONFIG_ARCH_EDB7211 is not set ++# CONFIG_ARCH_FORTUNET is not set ++# CONFIG_ARCH_GUIDEA07 is not set ++# CONFIG_ARCH_P720T is not set ++# CONFIG_ARCH_EP7211 is not set ++# CONFIG_ARCH_EP7212 is not set ++# CONFIG_ARCH_ACORN is not set ++# CONFIG_PLD is not set ++# CONFIG_FOOTBRIDGE is not set ++# CONFIG_FOOTBRIDGE_HOST is not set ++# CONFIG_FOOTBRIDGE_ADDIN is not set ++CONFIG_CPU_32=y ++# CONFIG_CPU_26 is not set ++# CONFIG_CPU_ARM610 is not set ++# CONFIG_CPU_ARM710 is not set ++# CONFIG_CPU_ARM720T is not set ++# CONFIG_CPU_ARM920T is not set ++# CONFIG_CPU_ARM922T is not set ++# CONFIG_CPU_ARM926T is not set ++# CONFIG_CPU_ARM1020 is not set ++# CONFIG_CPU_ARM1020E is not set ++# CONFIG_CPU_ARM1022 is not set ++# CONFIG_CPU_ARM1026 is not set ++# CONFIG_CPU_SA110 is not set ++# CONFIG_CPU_SA1100 is not set ++CONFIG_CPU_32v5=y ++CONFIG_CPU_XSCALE=y ++# CONFIG_XSCALE_CACHE_ERRATA is not set ++# CONFIG_CPU_32v3 is not set ++# CONFIG_CPU_32v4 is not set ++# CONFIG_DISCONTIGMEM is not set ++ ++# ++# General setup ++# ++# CONFIG_PCI is not set ++# CONFIG_ISA is not set ++# CONFIG_ISA_DMA is not set ++CONFIG_ZBOOT_ROM=y ++CONFIG_ZBOOT_ROM_TEXT=00040000 ++CONFIG_ZBOOT_ROM_BSS=a00c0000 ++CONFIG_CPU_FREQ=y ++CONFIG_HOTPLUG=y ++ ++# ++# PCMCIA/CardBus support ++# ++CONFIG_PCMCIA=y ++# CONFIG_I82092 is not set ++# CONFIG_I82365 is not set ++# CONFIG_TCIC is not set ++# CONFIG_PCMCIA_CLPS6700 is not set ++# CONFIG_PCMCIA_SA1100 is not set ++CONFIG_PCMCIA_PXA=y ++ ++# ++# MMC device drivers ++# ++CONFIG_MMC=m ++CONFIG_MMC_PXA=m ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_PARTITIONS=y ++CONFIG_NET=y ++CONFIG_SYSVIPC=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++CONFIG_SYSCTL=y ++# CONFIG_XIP_KERNEL is not set ++CONFIG_FPE_NWFPE=y ++# CONFIG_FPE_NWFPE_XP is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_KCORE_ELF=y ++# CONFIG_KCORE_AOUT is not set ++# CONFIG_BINFMT_AOUT is not set ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_PM=y ++CONFIG_APM=y ++# CONFIG_ARTHUR is not set ++CONFIG_CMDLINE="debug" ++CONFIG_ALIGNMENT_TRAP=y ++ ++# ++# Parallel port support ++# ++# CONFIG_PARPORT is not set ++ ++# ++# Memory Technology Devices (MTD) ++# ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_CONCAT is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++CONFIG_MTD_CFI=y ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_GEN_PROBE=y ++CONFIG_MTD_CFI_ADV_OPTIONS=y ++CONFIG_MTD_CFI_NOSWAP=y ++# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set ++# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set ++CONFIG_MTD_CFI_GEOMETRY=y ++# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++# CONFIG_MTD_CFI_I1 is not set ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++CONFIG_MTD_CFI_INTELEXT=y ++# CONFIG_MTD_CFI_AMDSTD is not set ++# CONFIG_MTD_CFI_STAA is not set ++CONFIG_MTD_CFI_UTIL=y ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++# CONFIG_MTD_OBSOLETE_CHIPS is not set ++# CONFIG_MTD_AMDSTD is not set ++# CONFIG_MTD_SHARP is not set ++# CONFIG_MTD_JEDEC is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PHYSMAP is not set ++# CONFIG_MTD_ARM_INTEGRATOR is not set ++# CONFIG_MTD_CDB89712 is not set ++# CONFIG_MTD_SA1100 is not set ++# CONFIG_MTD_DC21285 is not set ++# CONFIG_MTD_IQ80310 is not set ++# CONFIG_MTD_LUBBOCK is not set ++CONFIG_MTD_RAMSES=y ++# CONFIG_MTD_IXP425 is not set ++# CONFIG_MTD_EPXA10DB is not set ++# CONFIG_MTD_FORTUNET is not set ++# CONFIG_MTD_AUTCPU12 is not set ++# CONFIG_MTD_EDB7312 is not set ++# CONFIG_MTD_H720X is not set ++# CONFIG_MTD_IMPA7 is not set ++# CONFIG_MTD_CEIVA is not set ++# CONFIG_MTD_NOR_TOTO is not set ++# CONFIG_MTD_PCI is not set ++# CONFIG_MTD_PCMCIA is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLKMTD is not set ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++# CONFIG_MTD_DOCPROBE is not set ++# CONFIG_MTD_DOCECC is not set ++ ++# ++# NAND Flash Device Drivers ++# ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_NAND_SPIA is not set ++# CONFIG_MTD_NAND_TOTO is not set ++# CONFIG_MTD_NAND_AUTCPU12 is not set ++# CONFIG_MTD_NAND_EDB7312 is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++ ++# ++# Plug and Play configuration ++# ++# CONFIG_PNP is not set ++# CONFIG_ISAPNP is not set ++ ++# ++# Block devices ++# ++# CONFIG_BLK_DEV_FD is not set ++# CONFIG_BLK_DEV_XD is not set ++# CONFIG_PARIDE is not set ++# CONFIG_BLK_CPQ_DA is not set ++# CONFIG_BLK_CPQ_CISS_DA is not set ++# CONFIG_CISS_SCSI_TAPE is not set ++# CONFIG_BLK_DEV_DAC960 is not set ++# CONFIG_BLK_DEV_UMEM is not set ++CONFIG_BLK_DEV_LOOP=m ++CONFIG_BLK_DEV_NBD=m ++# CONFIG_BLK_DEV_RAM is not set ++# CONFIG_BLK_DEV_INITRD is not set ++CONFIG_BLK_STATS=y ++ ++# ++# Multi-device support (RAID and LVM) ++# ++# CONFIG_MD is not set ++# CONFIG_BLK_DEV_MD is not set ++# CONFIG_MD_LINEAR is not set ++# CONFIG_MD_RAID0 is not set ++# CONFIG_MD_RAID1 is not set ++# CONFIG_MD_RAID5 is not set ++# CONFIG_MD_MULTIPATH is not set ++# CONFIG_BLK_DEV_LVM is not set ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_NETLINK_DEV=m ++# CONFIG_NETFILTER is not set ++# CONFIG_FILTER is not set ++CONFIG_UNIX=y ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE=m ++# CONFIG_ARPD is not set ++# CONFIG_INET_ECN is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_IPV6 is not set ++# CONFIG_KHTTPD is not set ++# CONFIG_ATM is not set ++CONFIG_VLAN_8021Q=m ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++ ++# ++# Appletalk devices ++# ++# CONFIG_DEV_APPLETALK is not set ++# CONFIG_DECNET is not set ++CONFIG_BRIDGE=m ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_LLC is not set ++# CONFIG_NET_DIVERT is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_FASTROUTE is not set ++# CONFIG_NET_HW_FLOWCONTROL is not set ++ ++# ++# QoS and/or fair queueing ++# ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++ ++# ++# Network device support ++# ++CONFIG_NETDEVICES=y ++ ++# ++# ARCnet devices ++# ++# CONFIG_ARCNET is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_ETHERTAP is not set ++ ++# ++# Ethernet (10 or 100Mbit) ++# ++CONFIG_NET_ETHERNET=y ++# CONFIG_ARM_AM79C961A is not set ++# CONFIG_ARM_CIRRUS is not set ++# CONFIG_SUNLANCE is not set ++# CONFIG_SUNBMAC is not set ++# CONFIG_SUNQE is not set ++# CONFIG_SUNGEM is not set ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_LANCE is not set ++CONFIG_NET_VENDOR_SMC=y ++# CONFIG_WD80x3 is not set ++# CONFIG_ULTRAMCA is not set ++# CONFIG_ULTRA is not set ++# CONFIG_ULTRA32 is not set ++# CONFIG_SMC9194 is not set ++CONFIG_SMC91X=y ++# CONFIG_NET_VENDOR_RACAL is not set ++# CONFIG_NET_ISA is not set ++# CONFIG_NET_PCI is not set ++# CONFIG_NET_POCKET is not set ++ ++# ++# Ethernet (1000 Mbit) ++# ++# CONFIG_ACENIC is not set ++# CONFIG_DL2K is not set ++# CONFIG_E1000 is not set ++# CONFIG_MYRI_SBUS is not set ++# CONFIG_NS83820 is not set ++# CONFIG_HAMACHI is not set ++# CONFIG_YELLOWFIN is not set ++# CONFIG_R8169 is not set ++# CONFIG_SK98LIN is not set ++# CONFIG_TIGON3 is not set ++# CONFIG_FDDI is not set ++# CONFIG_HIPPI is not set ++# CONFIG_PLIP is not set ++CONFIG_PPP=m ++# CONFIG_PPP_MULTILINK is not set ++# CONFIG_PPP_FILTER is not set ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPPOE=m ++# CONFIG_SLIP is not set ++ ++# ++# Wireless LAN (non-hamradio) ++# ++CONFIG_NET_RADIO=y ++# CONFIG_STRIP is not set ++# CONFIG_WAVELAN is not set ++# CONFIG_ARLAN is not set ++# CONFIG_AIRONET4500 is not set ++# CONFIG_AIRONET4500_NONCS is not set ++# CONFIG_AIRONET4500_PROC is not set ++CONFIG_HERMES=m ++CONFIG_PCMCIA_HERMES=m ++# CONFIG_AIRO_CS is not set ++CONFIG_NET_WIRELESS=y ++ ++# ++# Token Ring devices ++# ++# CONFIG_TR is not set ++# CONFIG_NET_FC is not set ++# CONFIG_RCPCI is not set ++# CONFIG_SHAPER is not set ++ ++# ++# Wan interfaces ++# ++# CONFIG_WAN is not set ++ ++# ++# PCMCIA network device support ++# ++CONFIG_NET_PCMCIA=y ++# CONFIG_PCMCIA_3C589 is not set ++# CONFIG_PCMCIA_3C574 is not set ++# CONFIG_PCMCIA_FMVJ18X is not set ++# CONFIG_PCMCIA_PCNET is not set ++# CONFIG_PCMCIA_AXNET is not set ++# CONFIG_PCMCIA_NMCLAN is not set ++# CONFIG_PCMCIA_SMC91C92 is not set ++# CONFIG_PCMCIA_XIRC2PS is not set ++# CONFIG_ARCNET_COM20020_CS is not set ++# CONFIG_PCMCIA_IBMTR is not set ++# CONFIG_NET_PCMCIA_RADIO is not set ++ ++# ++# Amateur Radio support ++# ++# CONFIG_HAMRADIO is not set ++ ++# ++# IrDA (infrared) support ++# ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRDA_DEBUG=y ++ ++# ++# Infrared-port device drivers ++# ++CONFIG_IRTTY_SIR=m ++CONFIG_IRPORT_SIR=m ++# CONFIG_DONGLE is not set ++# CONFIG_USB_IRDA is not set ++# CONFIG_NSC_FIR is not set ++# CONFIG_WINBOND_FIR is not set ++# CONFIG_TOSHIBA_OLD is not set ++# CONFIG_TOSHIBA_FIR is not set ++# CONFIG_SMC_IRCC_FIR is not set ++# CONFIG_ALI_FIR is not set ++# CONFIG_VLSI_FIR is not set ++CONFIG_PXA_FIR=m ++ ++# ++# ATA/ATAPI/MFM/RLL support ++# ++# CONFIG_IDE is not set ++# CONFIG_BLK_DEV_IDE_MODES is not set ++# CONFIG_BLK_DEV_HD is not set ++ ++# ++# SCSI support ++# ++CONFIG_SCSI=m ++CONFIG_BLK_DEV_SD=m ++CONFIG_SD_EXTRA_DEVS=4 ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_SCSI_DEBUG_QUEUES is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++ ++# ++# SCSI low-level drivers ++# ++# CONFIG_SCSI_7000FASST is not set ++# CONFIG_SCSI_ACARD is not set ++# CONFIG_SCSI_AHA152X is not set ++# CONFIG_SCSI_AHA1542 is not set ++# CONFIG_SCSI_AHA1740 is not set ++# CONFIG_SCSI_AACRAID is not set ++# CONFIG_SCSI_AIC7XXX is not set ++# CONFIG_SCSI_AIC79XX is not set ++# CONFIG_SCSI_AIC7XXX_OLD is not set ++# CONFIG_SCSI_DPT_I2O is not set ++# CONFIG_SCSI_ADVANSYS is not set ++# CONFIG_SCSI_IN2000 is not set ++# CONFIG_SCSI_AM53C974 is not set ++# CONFIG_SCSI_MEGARAID is not set ++# CONFIG_SCSI_BUSLOGIC is not set ++# CONFIG_SCSI_DMX3191D is not set ++# CONFIG_SCSI_DTC3280 is not set ++# CONFIG_SCSI_EATA is not set ++# CONFIG_SCSI_EATA_DMA is not set ++# CONFIG_SCSI_EATA_PIO is not set ++# CONFIG_SCSI_FUTURE_DOMAIN is not set ++# CONFIG_SCSI_GDTH is not set ++# CONFIG_SCSI_GENERIC_NCR5380 is not set ++# CONFIG_SCSI_INITIO is not set ++# CONFIG_SCSI_INIA100 is not set ++# CONFIG_SCSI_NCR53C406A is not set ++# CONFIG_SCSI_NCR53C7xx is not set ++# CONFIG_SCSI_PAS16 is not set ++# CONFIG_SCSI_PCI2000 is not set ++# CONFIG_SCSI_PCI2220I is not set ++# CONFIG_SCSI_PSI240I is not set ++# CONFIG_SCSI_QLOGIC_FAS is not set ++# CONFIG_SCSI_SIM710 is not set ++# CONFIG_SCSI_SYM53C416 is not set ++# CONFIG_SCSI_T128 is not set ++# CONFIG_SCSI_U14_34F is not set ++# CONFIG_SCSI_NSP32 is not set ++# CONFIG_SCSI_DEBUG is not set ++ ++# ++# PCMCIA SCSI adapter support ++# ++# CONFIG_SCSI_PCMCIA is not set ++ ++# ++# I2O device support ++# ++# CONFIG_I2O is not set ++# CONFIG_I2O_BLOCK is not set ++# CONFIG_I2O_LAN is not set ++# CONFIG_I2O_SCSI is not set ++# CONFIG_I2O_PROC is not set ++ ++# ++# ISDN subsystem ++# ++# CONFIG_ISDN is not set ++ ++# ++# Input core support ++# ++CONFIG_INPUT=y ++CONFIG_INPUT_KEYBDEV=y ++CONFIG_INPUT_RAMSES_KEYB=y ++CONFIG_INPUT_RAMSES_WEDGE=y ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_MX1TS is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_SERIAL=y ++CONFIG_SERIAL_CONSOLE=y ++# CONFIG_SERIAL_EXTENDED is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_ANAKIN is not set ++# CONFIG_SERIAL_ANAKIN_CONSOLE is not set ++# CONFIG_SERIAL_AMBA is not set ++# CONFIG_SERIAL_AMBA_CONSOLE is not set ++# CONFIG_SERIAL_CLPS711X is not set ++# CONFIG_SERIAL_CLPS711X_CONSOLE is not set ++# CONFIG_SERIAL_21285 is not set ++# CONFIG_SERIAL_21285_OLD is not set ++# CONFIG_SERIAL_21285_CONSOLE is not set ++# CONFIG_SERIAL_UART00 is not set ++# CONFIG_SERIAL_UART00_CONSOLE is not set ++# CONFIG_SERIAL_SA1100 is not set ++# CONFIG_SERIAL_SA1100_CONSOLE is not set ++# CONFIG_SERIAL_OMAHA is not set ++# CONFIG_SERIAL_OMAHA_CONSOLE is not set ++# CONFIG_SERIAL_AT91 is not set ++# CONFIG_SERIAL_AT91_CONSOLE is not set ++# CONFIG_SERIAL_8250 is not set ++# CONFIG_SERIAL_8250_CONSOLE is not set ++# CONFIG_SERIAL_8250_EXTENDED is not set ++# CONFIG_SERIAL_8250_MANY_PORTS is not set ++# CONFIG_SERIAL_8250_SHARE_IRQ is not set ++# CONFIG_SERIAL_8250_DETECT_IRQ is not set ++# CONFIG_SERIAL_8250_MULTIPORT is not set ++# CONFIG_SERIAL_8250_HUB6 is not set ++CONFIG_UNIX98_PTYS=y ++CONFIG_UNIX98_PTY_COUNT=32 ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++CONFIG_I2C_PXA_ALGO=y ++CONFIG_I2C_PXA_ADAP=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_PROC=m ++# CONFIG_I2C_DS1307 is not set ++CONFIG_I2C_DS1337=y ++ ++# ++# L3 serial bus support ++# ++# CONFIG_L3 is not set ++# CONFIG_L3_ALGOBIT is not set ++# CONFIG_L3_BIT_SA1100_GPIO is not set ++# CONFIG_L3_SA1111 is not set ++# CONFIG_BIT_SA1100_GPIO is not set ++ ++# ++# Mice ++# ++# CONFIG_BUSMOUSE is not set ++# CONFIG_MOUSE is not set ++ ++# ++# Joysticks ++# ++# CONFIG_INPUT_GAMEPORT is not set ++# CONFIG_INPUT_NS558 is not set ++# CONFIG_INPUT_LIGHTNING is not set ++# CONFIG_INPUT_PCIGAME is not set ++# CONFIG_INPUT_CS461X is not set ++# CONFIG_INPUT_EMU10K1 is not set ++# CONFIG_INPUT_SERIO is not set ++# CONFIG_INPUT_SERPORT is not set ++# CONFIG_INPUT_ANALOG is not set ++# CONFIG_INPUT_A3D is not set ++# CONFIG_INPUT_ADI is not set ++# CONFIG_INPUT_COBRA is not set ++# CONFIG_INPUT_GF2K is not set ++# CONFIG_INPUT_GRIP is not set ++# CONFIG_INPUT_INTERACT is not set ++# CONFIG_INPUT_TMDC is not set ++# CONFIG_INPUT_SIDEWINDER is not set ++# CONFIG_INPUT_IFORCE_USB is not set ++# CONFIG_INPUT_IFORCE_232 is not set ++# CONFIG_INPUT_WARRIOR is not set ++# CONFIG_INPUT_MAGELLAN is not set ++# CONFIG_INPUT_SPACEORB is not set ++# CONFIG_INPUT_SPACEBALL is not set ++# CONFIG_INPUT_STINGER is not set ++# CONFIG_INPUT_DB9 is not set ++# CONFIG_INPUT_GAMECON is not set ++# CONFIG_INPUT_TURBOGRAFX is not set ++# CONFIG_QIC02_TAPE is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_IPMI_PANIC_EVENT is not set ++# CONFIG_IPMI_DEVICE_INTERFACE is not set ++# CONFIG_IPMI_KCS is not set ++# CONFIG_IPMI_WATCHDOG is not set ++ ++# ++# Watchdog Cards ++# ++# CONFIG_WATCHDOG is not set ++# CONFIG_SCx200_GPIO is not set ++# CONFIG_AMD_PM768 is not set ++# CONFIG_NVRAM is not set ++CONFIG_RTC=m ++CONFIG_PXA_RTC=m ++# CONFIG_DTLK is not set ++# CONFIG_R3964 is not set ++# CONFIG_APPLICOM is not set ++ ++# ++# Ftape, the floppy tape device driver ++# ++# CONFIG_FTAPE is not set ++# CONFIG_AGP is not set ++# CONFIG_DRM is not set ++ ++# ++# PCMCIA character devices ++# ++CONFIG_PCMCIA_SERIAL_CS=m ++# CONFIG_SYNCLINK_CS is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=m ++ ++# ++# Video For Linux ++# ++CONFIG_VIDEO_PROC_FS=y ++# CONFIG_I2C_PARPORT is not set ++# CONFIG_VIDEO_BT848 is not set ++# CONFIG_VIDEO_PMS is not set ++# CONFIG_VIDEO_CPIA is not set ++# CONFIG_VIDEO_SAA5249 is not set ++# CONFIG_TUNER_3036 is not set ++# CONFIG_VIDEO_STRADIS is not set ++# CONFIG_VIDEO_ZORAN is not set ++# CONFIG_VIDEO_ZORAN_BUZ is not set ++# CONFIG_VIDEO_ZORAN_DC10 is not set ++# CONFIG_VIDEO_ZORAN_LML33 is not set ++# CONFIG_VIDEO_ZR36120 is not set ++# CONFIG_VIDEO_MEYE is not set ++# CONFIG_VIDEO_CYBERPRO is not set ++ ++# ++# Radio Adapters ++# ++# CONFIG_RADIO_GEMTEK_PCI is not set ++# CONFIG_RADIO_MAXIRADIO is not set ++# CONFIG_RADIO_MAESTRO is not set ++# CONFIG_RADIO_MIROPCM20 is not set ++ ++# ++# File systems ++# ++# CONFIG_QUOTA is not set ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_REISERFS_CHECK is not set ++# CONFIG_REISERFS_PROC_INFO is not set ++# CONFIG_ADFS_FS is not set ++# CONFIG_ADFS_FS_RW is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BEFS_DEBUG is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_JBD is not set ++# CONFIG_JBD_DEBUG is not set ++CONFIG_FAT_FS=m ++CONFIG_MSDOS_FS=m ++# CONFIG_UMSDOS_FS is not set ++CONFIG_VFAT_FS=m ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++CONFIG_JFFS2_ZLIB=y ++CONFIG_JFFS2_RTIME=y ++CONFIG_JFFS2_RUBIN=y ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_LZARI is not set ++# CONFIG_JFFS2_CMODE_NONE is not set ++CONFIG_JFFS2_CMODE_PRIORITY=y ++# CONFIG_JFFS2_CMODE_SIZE is not set ++CONFIG_JFFS2_PROC=y ++CONFIG_CRAMFS=m ++# CONFIG_CRAMFS_LINEAR is not set ++# CONFIG_CRAMFS_LINEAR_XIP is not set ++# CONFIG_ROOT_CRAMFS_LINEAR is not set ++CONFIG_TMPFS=y ++CONFIG_RAMFS=y ++# CONFIG_ISO9660_FS is not set ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_JFS_DEBUG is not set ++# CONFIG_JFS_STATISTICS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_NTFS_FS is not set ++# CONFIG_NTFS_RW is not set ++# CONFIG_HPFS_FS is not set ++CONFIG_PROC_FS=y ++CONFIG_DEVFS_FS=y ++CONFIG_DEVFS_MOUNT=y ++# CONFIG_DEVFS_DEBUG is not set ++# CONFIG_DEVPTS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX4FS_RW is not set ++# CONFIG_ROMFS_FS is not set ++CONFIG_EXT2_FS=m ++# CONFIG_SYSV_FS is not set ++# CONFIG_UDF_FS is not set ++# CONFIG_UDF_RW is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_UFS_FS_WRITE is not set ++ ++# ++# Network File Systems ++# ++# CONFIG_CODA_FS is not set ++# CONFIG_INTERMEZZO_FS is not set ++CONFIG_NFS_FS=y ++# CONFIG_NFS_V3 is not set ++CONFIG_ROOT_NFS=y ++# CONFIG_NFSD is not set ++# CONFIG_NFSD_V3 is not set ++# CONFIG_NFSD_TCP is not set ++CONFIG_SUNRPC=y ++CONFIG_LOCKD=y ++# CONFIG_SMB_FS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_NCPFS_PACKET_SIGNING is not set ++# CONFIG_NCPFS_IOCTL_LOCKING is not set ++# CONFIG_NCPFS_STRONG is not set ++# CONFIG_NCPFS_NFS_NS is not set ++# CONFIG_NCPFS_OS2_NS is not set ++# CONFIG_NCPFS_SMALLDOS is not set ++# CONFIG_NCPFS_NLS is not set ++# CONFIG_NCPFS_EXTRAS is not set ++# CONFIG_ZISOFS_FS is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_SMB_NLS is not set ++CONFIG_NLS=y ++ ++# ++# Native Language Support ++# ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=m ++CONFIG_NLS_CODEPAGE_737=m ++CONFIG_NLS_CODEPAGE_775=m ++CONFIG_NLS_CODEPAGE_850=m ++CONFIG_NLS_CODEPAGE_852=m ++CONFIG_NLS_CODEPAGE_855=m ++CONFIG_NLS_CODEPAGE_857=m ++CONFIG_NLS_CODEPAGE_860=m ++CONFIG_NLS_CODEPAGE_861=m ++CONFIG_NLS_CODEPAGE_862=m ++CONFIG_NLS_CODEPAGE_863=m ++CONFIG_NLS_CODEPAGE_864=m ++CONFIG_NLS_CODEPAGE_865=m ++CONFIG_NLS_CODEPAGE_866=m ++CONFIG_NLS_CODEPAGE_869=m ++CONFIG_NLS_CODEPAGE_936=m ++CONFIG_NLS_CODEPAGE_950=m ++CONFIG_NLS_CODEPAGE_932=m ++CONFIG_NLS_CODEPAGE_949=m ++CONFIG_NLS_CODEPAGE_874=m ++CONFIG_NLS_ISO8859_8=m ++CONFIG_NLS_CODEPAGE_1250=m ++CONFIG_NLS_CODEPAGE_1251=m ++CONFIG_NLS_ISO8859_1=m ++CONFIG_NLS_ISO8859_2=m ++CONFIG_NLS_ISO8859_3=m ++CONFIG_NLS_ISO8859_4=m ++CONFIG_NLS_ISO8859_5=m ++CONFIG_NLS_ISO8859_6=m ++CONFIG_NLS_ISO8859_7=m ++CONFIG_NLS_ISO8859_9=m ++CONFIG_NLS_ISO8859_13=m ++CONFIG_NLS_ISO8859_14=m ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_KOI8_R=m ++CONFIG_NLS_KOI8_U=m ++CONFIG_NLS_UTF8=m ++ ++# ++# Console drivers ++# ++CONFIG_PC_KEYMAP=y ++# CONFIG_VGA_CONSOLE is not set ++ ++# ++# Frame-buffer support ++# ++CONFIG_FB=y ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FB_ACORN is not set ++# CONFIG_FB_ANAKIN is not set ++# CONFIG_FB_CLPS711X is not set ++# CONFIG_FB_SA1100 is not set ++# CONFIG_FB_DBMX1 is not set ++CONFIG_FB_PXA=y ++# CONFIG_FB_PXA_8BPP is not set ++CONFIG_FB_PXA_16BPP=y ++# CONFIG_FB_CYBER2000 is not set ++# CONFIG_FB_VIRTUAL is not set ++CONFIG_FBCON_ADVANCED=y ++# CONFIG_FBCON_MFB is not set ++# CONFIG_FBCON_CFB2 is not set ++# CONFIG_FBCON_CFB4 is not set ++# CONFIG_FBCON_CFB8 is not set ++CONFIG_FBCON_CFB16=y ++# CONFIG_FBCON_CFB24 is not set ++# CONFIG_FBCON_CFB32 is not set ++# CONFIG_FBCON_AFB is not set ++# CONFIG_FBCON_ILBM is not set ++# CONFIG_FBCON_IPLAN2P2 is not set ++# CONFIG_FBCON_IPLAN2P4 is not set ++# CONFIG_FBCON_IPLAN2P8 is not set ++# CONFIG_FBCON_MAC is not set ++# CONFIG_FBCON_VGA_PLANES is not set ++# CONFIG_FBCON_VGA is not set ++# CONFIG_FBCON_HGA is not set ++# CONFIG_FBCON_FONTWIDTH8_ONLY is not set ++# CONFIG_FBCON_FONTS is not set ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++# CONFIG_SOUND_ALI5455 is not set ++# CONFIG_SOUND_BT878 is not set ++# CONFIG_SOUND_CMPCI is not set ++# CONFIG_SOUND_EMU10K1 is not set ++# CONFIG_MIDI_EMU10K1 is not set ++# CONFIG_SOUND_FUSION is not set ++# CONFIG_SOUND_CS4281 is not set ++# CONFIG_SOUND_ES1370 is not set ++# CONFIG_SOUND_ES1371 is not set ++# CONFIG_SOUND_ESSSOLO1 is not set ++# CONFIG_SOUND_MAESTRO is not set ++# CONFIG_SOUND_MAESTRO3 is not set ++# CONFIG_SOUND_FORTE is not set ++# CONFIG_SOUND_ICH is not set ++# CONFIG_SOUND_RME96XX is not set ++# CONFIG_SOUND_SONICVIBES is not set ++# CONFIG_SOUND_TRIDENT is not set ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++# CONFIG_SOUND_VIA82CXXX is not set ++# CONFIG_MIDI_VIA82CXXX is not set ++# CONFIG_SOUND_OSS is not set ++# CONFIG_SOUND_VIDC is not set ++# CONFIG_SOUND_WAVEARTIST is not set ++CONFIG_SOUND_PXA_AC97=y ++# CONFIG_SOUND_TVMIXER is not set ++ ++# ++# Multimedia Capabilities Port drivers ++# ++CONFIG_MCP=y ++# CONFIG_MCP_SA1100 is not set ++# CONFIG_MCP_UCB1200 is not set ++# CONFIG_MCP_UCB1200_AUDIO is not set ++# CONFIG_MCP_UCB1200_TS is not set ++CONFIG_MCP_UCB1400_TS=y ++ ++# ++# USB support ++# ++CONFIG_USB=m ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_DEVICEFS=y ++# CONFIG_USB_BANDWIDTH is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_UHCI is not set ++# CONFIG_USB_UHCI_ALT is not set ++# CONFIG_USB_OHCI is not set ++# CONFIG_USB_OHCI_SA1111 is not set ++CONFIG_USB_SL811HS_ALT=m ++CONFIG_USB_AUDIO=m ++CONFIG_USB_EMI26=m ++# CONFIG_USB_BLUETOOTH is not set ++CONFIG_USB_MIDI=m ++CONFIG_USB_STORAGE=m ++CONFIG_USB_STORAGE_DEBUG=y ++CONFIG_USB_STORAGE_DATAFAB=y ++CONFIG_USB_STORAGE_FREECOM=y ++# CONFIG_USB_STORAGE_ISD200 is not set ++CONFIG_USB_STORAGE_DPCM=y ++CONFIG_USB_STORAGE_HP8200e=y ++CONFIG_USB_STORAGE_SDDR09=y ++CONFIG_USB_STORAGE_SDDR55=y ++CONFIG_USB_STORAGE_JUMPSHOT=y ++# CONFIG_USB_ACM is not set ++CONFIG_USB_PRINTER=m ++CONFIG_USB_HID=m ++CONFIG_USB_HIDINPUT=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_KBD=m ++# CONFIG_USB_MOUSE is not set ++# CONFIG_USB_AIPTEK is not set ++# CONFIG_USB_WACOM is not set ++# CONFIG_USB_KBTAB is not set ++# CONFIG_USB_POWERMATE is not set ++CONFIG_USB_DC2XX=m ++CONFIG_USB_MDC800=m ++CONFIG_USB_SCANNER=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USB_HPUSBSCSI=m ++CONFIG_USB_IBMCAM=m ++CONFIG_USB_KONICAWC=m ++CONFIG_USB_OV511=m ++CONFIG_USB_PWC=m ++CONFIG_USB_SE401=m ++CONFIG_USB_STV680=m ++CONFIG_USB_VICAM=m ++# CONFIG_USB_DSBR is not set ++# CONFIG_USB_DABUSB is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_CDCETHER is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_USS720 is not set ++ ++# ++# USB Serial Converter support ++# ++CONFIG_USB_SERIAL=m ++# CONFIG_USB_SERIAL_DEBUG is not set ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_BELKIN=m ++CONFIG_USB_SERIAL_WHITEHEAT=m ++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m ++CONFIG_USB_SERIAL_EMPEG=m ++# CONFIG_USB_SERIAL_FTDI_SIO is not set ++# CONFIG_USB_SERIAL_VISOR is not set ++# CONFIG_USB_SERIAL_IPAQ is not set ++# CONFIG_USB_SERIAL_IR is not set ++# CONFIG_USB_SERIAL_EDGEPORT is not set ++# CONFIG_USB_SERIAL_EDGEPORT_TI is not set ++# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set ++# CONFIG_USB_SERIAL_KEYSPAN is not set ++# CONFIG_USB_SERIAL_MCT_U232 is not set ++# CONFIG_USB_SERIAL_KLSI is not set ++# CONFIG_USB_SERIAL_KOBIL_SCT is not set ++# CONFIG_USB_SERIAL_PL2303 is not set ++# CONFIG_USB_SERIAL_CYBERJACK is not set ++# CONFIG_USB_SERIAL_XIRCOM is not set ++# CONFIG_USB_SERIAL_OMNINET is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_TIGL is not set ++# CONFIG_USB_BRLVGER is not set ++# CONFIG_USB_LCD is not set ++ ++# ++# Bluetooth support ++# ++# CONFIG_BLUEZ is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_FRAME_POINTER=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_NO_PGT_CACHE is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_DEBUG_SLAB is not set ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_WAITQ is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_ERRORS is not set ++# CONFIG_DEBUG_LL is not set ++# CONFIG_DEBUG_DC21285_PORT is not set ++# CONFIG_DEBUG_CLPS711X_UART2 is not set ++ ++# ++# Library routines ++# ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++# CONFIG_REED_SOLOMON is not set +--- linux-2.4.21/arch/arm/mach-pxa/Makefile~pm ++++ linux-2.4.21/arch/arm/mach-pxa/Makefile +@@ -14,8 +14,11 @@ + obj-n := + obj- := + +-export-objs := generic.o irq.o dma.o sa1111.o \ +- usb_ctl.o usb_recv.o usb_send.o ++export-objs := apm.o generic.o irq.o dma.o sa1111.o \ ++ usb_ctl.o usb_recv.o usb_send.o pm.o ++ ++ ++export-objs += ramses.o + + # Common support (must be linked before board specific support) + obj-y += generic.o irq.o dma.o +@@ -27,6 +30,7 @@ + obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o + obj-$(CONFIG_ARCH_PXA_CERF) += cerf.o + obj-$(CONFIG_ARCH_PXA_IDP) += idp.o ++obj-$(CONFIG_ARCH_RAMSES) += ramses.o + obj-$(CONFIG_ARCH_TRIZEPS2) += trizeps2.o + + # Support for blinky lights +@@ -48,6 +52,7 @@ + + # Misc features + obj-$(CONFIG_PM) += pm.o sleep.o ++obj-$(CONFIG_APM) += apm.o + obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o + + include $(TOPDIR)/Rules.make +--- /dev/null ++++ linux-2.4.21/arch/arm/mach-pxa/apm.c +@@ -0,0 +1,491 @@ ++/* ++ * bios-less APM driver for ARM Linux ++ * Jamey Hicks ++ * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com) ++ * ++ * APM 1.2 Reference: ++ * Intel Corporation, Microsoft Corporation. Advanced Power Management ++ * (APM) BIOS Interface Specification, Revision 1.2, February 1996. ++ * ++ * [This document is available from Microsoft at: ++ * http://www.microsoft.com/hwdev/busbios/amp_12.htm] ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#ifdef CONFIG_SA1100_H3XXX ++#include ++#endif ++ ++#include "pm-common.c" ++ ++struct apm_bios_info apm_bios_info = { ++ /* this driver simulates APM version 1.2 */ ++ version: 0x102, ++ flags: APM_32_BIT_SUPPORT ++}; ++ ++/* ++ * The apm_bios device is one of the misc char devices. ++ * This is its minor number. ++ */ ++#define APM_MINOR_DEV 134 ++ ++/* ++ * See Documentation/Config.help for the configuration options. ++ * ++ * Various options can be changed at boot time as follows: ++ * (We allow underscores for compatibility with the modules code) ++ * apm=on/off enable/disable APM ++ * [no-]power[-_]off power off on shutdown ++ */ ++ ++/* ++ * Maximum number of events stored ++ */ ++#define APM_MAX_EVENTS 10 ++ ++/* ++ * The per-file APM data ++ */ ++struct apm_user { ++ int magic; ++ struct apm_user * next; ++ int suser: 1; ++ int suspend_wait: 1; ++ int suspend_result; ++ int suspends_pending; ++ int standbys_pending; ++ int suspends_read; ++ int standbys_read; ++ int event_head; ++ int event_tail; ++ apm_event_t events[APM_MAX_EVENTS]; ++}; ++ ++/* ++ * The magic number in apm_user ++ */ ++#define APM_BIOS_MAGIC 0x4101 ++ ++/* ++ * Local variables ++ */ ++ ++#ifdef CONFIG_APM_RTC_IS_GMT ++#define clock_cmos_diff 0 ++#define got_clock_diff 1 ++#endif ++static int apm_disabled; ++#ifdef CONFIG_SMP ++static int power_off; ++#else ++static int power_off = 1; ++#endif ++static int exit_kapmd; ++static int kapmd_running; ++ ++static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); ++static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); ++static struct apm_user * user_list = NULL; ++ ++static char driver_version[] = "1.13"; /* no spaces */ ++ ++typedef struct lookup_t { ++ int key; ++ char * msg; ++} lookup_t; ++ ++static const lookup_t error_table[] = { ++/* N/A { APM_SUCCESS, "Operation succeeded" }, */ ++ { APM_DISABLED, "Power management disabled" }, ++ { APM_CONNECTED, "Real mode interface already connected" }, ++ { APM_NOT_CONNECTED, "Interface not connected" }, ++ { APM_16_CONNECTED, "16 bit interface already connected" }, ++/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ ++ { APM_32_CONNECTED, "32 bit interface already connected" }, ++ { APM_32_UNSUPPORTED, "32 bit interface not supported" }, ++ { APM_BAD_DEVICE, "Unrecognized device ID" }, ++ { APM_BAD_PARAM, "Parameter out of range" }, ++ { APM_NOT_ENGAGED, "Interface not engaged" }, ++ { APM_BAD_FUNCTION, "Function not supported" }, ++ { APM_RESUME_DISABLED, "Resume timer disabled" }, ++ { APM_BAD_STATE, "Unable to enter requested state" }, ++/* N/A { APM_NO_EVENTS, "No events pending" }, */ ++ { APM_NO_ERROR, "BIOS did not set a return code" }, ++ { APM_NOT_PRESENT, "No APM present" } ++}; ++#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) ++ ++static int (*apm_get_power_status)(u_char *ac_line_status, ++ u_char *battery_status, ++ u_char *battery_flag, ++ u_char *battery_percentage, ++ u_short *battery_life) = 0; ++ ++void apm_register_get_power_status( int (*fn)(u_char *ac_line_status, ++ u_char *battery_status, ++ u_char *battery_flag, ++ u_char *battery_percentage, ++ u_short *battery_life)) ++{ ++ apm_get_power_status = fn; ++} ++ ++static int queue_empty(struct apm_user *as) ++{ ++ return as->event_head == as->event_tail; ++} ++ ++static apm_event_t get_queued_event(struct apm_user *as) ++{ ++ as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; ++ return as->events[as->event_tail]; ++} ++ ++static int check_apm_user(struct apm_user *as, const char *func) ++{ ++ if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { ++ printk(KERN_ERR "apm: %s passed bad filp\n", func); ++ return 1; ++ } ++ return 0; ++} ++ ++static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) ++{ ++ struct apm_user * as; ++ int i; ++ apm_event_t event; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ as = fp->private_data; ++ if (check_apm_user(as, "read")) ++ return -EIO; ++ if (count < sizeof(apm_event_t)) ++ return -EINVAL; ++ if (queue_empty(as)) { ++ if (fp->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ add_wait_queue(&apm_waitqueue, &wait); ++ printk("do_read: waiting\n"); ++repeat: ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (queue_empty(as) && !signal_pending(current)) { ++ schedule(); ++ goto repeat; ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&apm_waitqueue, &wait); ++ } ++ i = count; ++ while ((i >= sizeof(event)) && !queue_empty(as)) { ++ event = get_queued_event(as); ++ printk(" do_read: event=%d\n", event); ++ if (copy_to_user(buf, &event, sizeof(event))) { ++ if (i < count) ++ break; ++ return -EFAULT; ++ } ++ switch (event) { ++ case APM_SYS_SUSPEND: ++ case APM_USER_SUSPEND: ++ as->suspends_read++; ++ break; ++ ++ case APM_SYS_STANDBY: ++ case APM_USER_STANDBY: ++ as->standbys_read++; ++ break; ++ } ++ buf += sizeof(event); ++ i -= sizeof(event); ++ } ++ if (i < count) ++ return count - i; ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ return 0; ++} ++ ++static unsigned int do_poll(struct file *fp, poll_table * wait) ++{ ++ struct apm_user * as; ++ ++ as = fp->private_data; ++ if (check_apm_user(as, "poll")) ++ return 0; ++ poll_wait(fp, &apm_waitqueue, wait); ++ if (!queue_empty(as)) ++ return POLLIN | POLLRDNORM; ++ return 0; ++} ++ ++static int do_ioctl(struct inode * inode, struct file *filp, ++ u_int cmd, u_long arg) ++{ ++ struct apm_user * as; ++ ++ as = filp->private_data; ++ if (check_apm_user(as, "ioctl")) ++ return -EIO; ++ if (!as->suser) ++ return -EPERM; ++ switch (cmd) { ++ case APM_IOC_SUSPEND: ++ pm_suggest_suspend(); ++ break; ++ default: ++ printk("//hs %x\n", cmd); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int do_release(struct inode * inode, struct file * filp) ++{ ++ struct apm_user * as; ++ ++ as = filp->private_data; ++ if (check_apm_user(as, "release")) ++ return 0; ++ filp->private_data = NULL; ++ lock_kernel(); ++ unlock_kernel(); ++ kfree(as); ++ return 0; ++} ++ ++static int do_open(struct inode * inode, struct file * filp) ++{ ++ struct apm_user * as; ++ ++ as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); ++ if (as == NULL) { ++ printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n", ++ sizeof(*as)); ++ return -ENOMEM; ++ } ++ as->magic = APM_BIOS_MAGIC; ++ as->event_tail = as->event_head = 0; ++ as->suspends_pending = as->standbys_pending = 0; ++ as->suspends_read = as->standbys_read = 0; ++ /* ++ * XXX - this is a tiny bit broken, when we consider BSD ++ * process accounting. If the device is opened by root, we ++ * instantly flag that we used superuser privs. Who knows, ++ * we might close the device immediately without doing a ++ * privileged operation -- cevans ++ */ ++ as->suser = capable(CAP_SYS_ADMIN); ++ as->next = user_list; ++ user_list = as; ++ filp->private_data = as; ++ return 0; ++} ++ ++static int apm_get_info(char *buf, char **start, off_t fpos, int length) ++{ ++ char * p; ++ unsigned short dx; ++ unsigned short error; ++ unsigned char ac_line_status = 0xff; ++ unsigned char battery_status = 0xff; ++ unsigned char battery_flag = 0xff; ++ unsigned char percentage = 0xff; ++ int time_units = -1; ++ char *units = "?"; ++ ++ p = buf; ++ ++ if ( (smp_num_cpus == 1) && ++ apm_get_power_status && ++ !(error = apm_get_power_status(&ac_line_status, ++ &battery_status, &battery_flag, &percentage, &dx))) { ++ if (apm_bios_info.version > 0x100) { ++ if (dx != 0xffff) { ++ units = (dx & 0x8000) ? "min" : "sec"; ++ time_units = dx & 0x7fff; ++ } ++ } ++ } ++ /* Arguments, with symbols from linux/apm_bios.h. Information is ++ from the Get Power Status (0x0a) call unless otherwise noted. ++ ++ 0) Linux driver version (this will change if format changes) ++ 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. ++ 2) APM flags from APM Installation Check (0x00): ++ bit 0: APM_16_BIT_SUPPORT ++ bit 1: APM_32_BIT_SUPPORT ++ bit 2: APM_IDLE_SLOWS_CLOCK ++ bit 3: APM_BIOS_DISABLED ++ bit 4: APM_BIOS_DISENGAGED ++ 3) AC line status ++ 0x00: Off-line ++ 0x01: On-line ++ 0x02: On backup power (BIOS >= 1.1 only) ++ 0xff: Unknown ++ 4) Battery status ++ 0x00: High ++ 0x01: Low ++ 0x02: Critical ++ 0x03: Charging ++ 0x04: Selected battery not present (BIOS >= 1.2 only) ++ 0xff: Unknown ++ 5) Battery flag ++ bit 0: High ++ bit 1: Low ++ bit 2: Critical ++ bit 3: Charging ++ bit 7: No system battery ++ 0xff: Unknown ++ 6) Remaining battery life (percentage of charge): ++ 0-100: valid ++ -1: Unknown ++ 7) Remaining battery life (time units): ++ Number of remaining minutes or seconds ++ -1: Unknown ++ 8) min = minutes; sec = seconds */ ++ ++ p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", ++ driver_version, ++ (apm_bios_info.version >> 8) & 0xff, ++ apm_bios_info.version & 0xff, ++ apm_bios_info.flags, ++ ac_line_status, ++ battery_status, ++ battery_flag, ++ percentage, ++ time_units, ++ units); ++ ++ return p - buf; ++} ++ ++#ifndef MODULE ++static int __init apm_setup(char *str) ++{ ++ int invert; ++ ++printk("//hs apm_setup\n"); ++ while ((str != NULL) && (*str != '\0')) { ++ if (strncmp(str, "off", 3) == 0) ++ apm_disabled = 1; ++ if (strncmp(str, "on", 2) == 0) ++ apm_disabled = 0; ++ invert = (strncmp(str, "no-", 3) == 0); ++ if (invert) ++ str += 3; ++ if ((strncmp(str, "power-off", 9) == 0) || ++ (strncmp(str, "power_off", 9) == 0)) ++ power_off = !invert; ++ str = strchr(str, ','); ++ if (str != NULL) ++ str += strspn(str, ", \t"); ++ } ++ return 1; ++} ++ ++__setup("apm=", apm_setup); ++#endif ++ ++static struct file_operations apm_bios_fops = { ++ owner: THIS_MODULE, ++ read: do_read, ++ poll: do_poll, ++ ioctl: do_ioctl, ++ open: do_open, ++ release: do_release, ++}; ++ ++static struct miscdevice apm_device = { ++ APM_MINOR_DEV, ++ "apm_bios", ++ &apm_bios_fops ++}; ++ ++#define APM_INIT_ERROR_RETURN return -1 ++ ++/* ++ * Just start the APM thread. We do NOT want to do APM BIOS ++ * calls from anything but the APM thread, if for no other reason ++ * than the fact that we don't trust the APM BIOS. This way, ++ * most common APM BIOS problems that lead to protection errors ++ * etc will have at least some level of being contained... ++ * ++ * In short, if something bad happens, at least we have a choice ++ * of just killing the apm thread.. ++ */ ++static int __init apm_init(void) ++{ ++ if (apm_bios_info.version == 0) { ++ printk(KERN_INFO "apm: BIOS not found.\n"); ++ APM_INIT_ERROR_RETURN; ++ } ++ printk(KERN_INFO ++ "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n", ++ ((apm_bios_info.version >> 8) & 0xff), ++ (apm_bios_info.version & 0xff), ++ apm_bios_info.flags, ++ driver_version); ++ ++ if (apm_disabled) { ++ printk(KERN_NOTICE "apm: disabled on user request.\n"); ++ APM_INIT_ERROR_RETURN; ++ } ++ ++ if (PM_IS_ACTIVE()) { ++ printk(KERN_NOTICE "apm: overridden by ACPI.\n"); ++ APM_INIT_ERROR_RETURN; ++ } ++ pm_active = 1; ++ ++ create_proc_info_entry("apm", 0, NULL, apm_get_info); ++ ++ misc_register(&apm_device); ++ ++ return 0; ++} ++ ++module_init(apm_init); ++ ++#ifdef MODULE ++static void __exit apm_exit(void) ++{ ++ misc_deregister(&apm_device); ++ remove_proc_entry("apm", NULL); ++ if (power_off) ++ pm_power_off = NULL; ++ exit_kapmd = 1; ++ while (kapmd_running) ++ schedule(); ++ pm_active = 0; ++} ++ ++module_exit(apm_exit); ++ ++MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell"); ++MODULE_DESCRIPTION("A minimal emulation of APM"); ++MODULE_PARM(power_off, "i"); ++MODULE_PARM_DESC(power_off, "Enable power off"); ++#endif +--- linux-2.4.21/arch/arm/mach-pxa/cpu-pxa.c~ramses-corevolt ++++ linux-2.4.21/arch/arm/mach-pxa/cpu-pxa.c +@@ -39,7 +39,7 @@ + + #include + +-#define DEBUGGING 1 ++#define DEBUGGING 0 + + #if DEBUGGING + static unsigned int freq_debug = DEBUGGING; +@@ -52,6 +52,7 @@ + unsigned int khz; + unsigned int cccr; + unsigned int pxbus; ++ unsigned int corevolt; + } pxa_freqs_t; + + #define CCLKCFG_TURBO 0x1 +@@ -79,23 +80,23 @@ + + static pxa_freqs_t pxa250_valid_freqs[] = + { +- {199100, 0x141, 99}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ +- {298600, 0x1c1, 99}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ +- {398100, 0x241, 99}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ ++ {199100, 0x141, 99, 115}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ ++ {298600, 0x1c1, 99, 125}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ ++ {398100, 0x241, 99, 135}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ + {0,0} + }; + + static pxa_freqs_t pxa255_valid_freqs[] = + { +- { 99000, 0x121, 50}, /* mem= 99, run= 99, turbo= 99, PXbus= 50 */ +-OC( {118000, 0x122, 59},)/* mem=118, run=118, turbo=118, PXbus= 59 OC'd mem */ +- {199100, 0x141, 99}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ +-OC( {236000, 0x142,118},)/* mem=118, run=236, turbo=236, PXbus=118 OC'd mem */ +- {298600, 0x1c1, 99}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ +-OC( {354000, 0x1c2,118},)/* mem=118, run=236, turbo=354, PXbus=118 OC'd mem */ +- {398099, 0x241, 99}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ +- {398100, 0x161,196}, /* mem= 99, run=398, turbo=398, PXbus=196 */ +-OC( {471000, 0x162,236},)/* mem=118, run=471, turbo=471, PXbus=236 OC'd mem/core/bus */ ++ { 99000, 0x121, 50, 105}, /* mem= 99, run= 99, turbo= 99, PXbus= 50 */ ++OC( {118000, 0x122, 59, 115},)/* mem=118, run=118, turbo=118, PXbus= 59 OC'd mem */ ++ {199100, 0x141, 99, 115}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ ++OC( {236000, 0x142,118, 125},)/* mem=118, run=236, turbo=236, PXbus=118 OC'd mem */ ++ {298600, 0x1c1, 99, 125}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ ++OC( {354000, 0x1c2,118, 135},)/* mem=118, run=236, turbo=354, PXbus=118 OC'd mem */ ++ {398099, 0x241, 99, 135}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ ++ {398100, 0x161,196, 135}, /* mem= 99, run=398, turbo=398, PXbus=196 */ ++OC( {471000, 0x162,236, 150},)/* mem=118, run=471, turbo=471, PXbus=236 OC'd mem/core/bus */ + {0,0} + }; + +@@ -109,7 +110,7 @@ + int i=0; + while( pxa_valid_freqs[i].khz) + { +- if( pxa_valid_freqs[i].khz == khz) ++ if (pxa_valid_freqs[i].khz == khz) + return &pxa_valid_freqs[i]; + i++; + } +@@ -141,14 +142,17 @@ + void *ramstart = phys_to_virt(0xa0000000); + pxa_freqs_t *freq_info; + +- if( ! supported) return; ++ if (! supported) return; + + freq_info = pxa_get_freq_info( khz); + +- if( ! freq_info) return; ++ if (! freq_info) return; ++ ++ if (freq_info->corevolt > ramses_corevolt_shadow) ++ ramses_set_corevolt(freq_info->corevolt); + + CCCR = freq_info->cccr; +- if( freq_debug) ++ if (freq_debug) + printk(KERN_INFO "Changing CPU frequency to %d Mhz (PXbus=%dMhz).\n", + khz/1000, freq_info->pxbus); + +@@ -184,6 +188,9 @@ + : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart) + : "r4", "r5"); + local_irq_restore(flags); ++ ++ if (freq_info->corevolt < ramses_corevolt_shadow) ++ ramses_set_corevolt(freq_info->corevolt); + } + + static int pxa_init_freqs( void) +@@ -191,19 +198,19 @@ + int cpu_ver; + asm("mrc%? p15, 0, %0, c0, c0" : "=r" (cpu_ver)); + +- if( (cpu_ver & 0xf) <= PXA250_REV_A1) ++ if ((cpu_ver & 0xf) <= PXA250_REV_A1) + { + return 0; + } + +- if( (cpu_ver & 0xf) <= PXA250_REV_B2) ++ if ((cpu_ver & 0xf) <= PXA250_REV_B2) + { +- if( freq_debug) printk(KERN_INFO "Using PXA250 frequency points.\n"); ++ if (freq_debug) printk(KERN_INFO "Using PXA250 frequency points.\n"); + pxa_valid_freqs = pxa250_valid_freqs; + } + else /* C0 and above */ + { +- if( freq_debug) printk(KERN_INFO "Using PXA255 frequency points.\n"); ++ if (freq_debug) printk(KERN_INFO "Using PXA255 frequency points.\n"); + pxa_valid_freqs = pxa255_valid_freqs; + } + +@@ -212,24 +219,23 @@ + + static int __init pxa_clk_init(void) + { +- if( pxa_init_freqs()) ++ if (pxa_init_freqs()) + { +- if( freq_debug) printk(KERN_INFO "Registering CPU frequency change support.\n"); ++ if (freq_debug) printk(KERN_INFO "Registering CPU frequency change support.\n"); + supported = 1; + + cpufreq_init( get_clk_frequency_khz(0), PXA25x_MIN_FREQ, PXA25x_MAX_FREQ); +- cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); + } + else + { +- if( freq_debug) printk(KERN_INFO "Disabling CPU frequency change support.\n"); ++ if (freq_debug) printk(KERN_INFO "Disabling CPU frequency change support.\n"); + /* Note that we have to initialize the generic code in order to + * release a lock (cpufreq_sem). Any registration for freq changes + * (e.g. lcd driver) will get blocked otherwise. + */ + cpufreq_init( 0, 0, 0); +- cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); + } ++ cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); + + return 0; + } +--- linux-2.4.21/arch/arm/mach-pxa/generic.c~pm ++++ linux-2.4.21/arch/arm/mach-pxa/generic.c +@@ -28,6 +28,11 @@ + #include + #include + ++#ifdef CONFIG_PXA_RTC_HACK ++#include ++#include ++#endif ++ + #include "generic.h" + + /* +@@ -139,4 +144,41 @@ + { + iotable_init(standard_io_desc); + get_clk_frequency_khz( 1); ++#ifdef CONFIG_PXA_RTC_HACK ++ pxa_rtc_hack_init(); ++#endif ++} ++ ++ ++ ++#ifdef CONFIG_PXA_RTC_HACK ++unsigned long *save_RCNR = 0; ++ ++void pxa_rtc_hack_init(void) ++{ ++ /* ++ This has to be here since I guess the bootmem API is the ++ right choice to allocate the memory during boot ++ place. And we are sure that timer iqr is not already ++ running. ++ - Christian Pellegin ++ */ ++ unsigned long pxa_rtc_hack = 0; ++ ++ pxa_rtc_hack = meminfo.bank[meminfo.nr_banks-1].start + ++ meminfo.bank[meminfo.nr_banks-1].size - ++ PAGE_SIZE; ++ reserve_bootmem(pxa_rtc_hack, PAGE_SIZE); ++ printk("Reserved %ld bytes at %lx for RTC hack\n", ++ PAGE_SIZE, pxa_rtc_hack); ++ save_RCNR = (unsigned long *) phys_to_virt(pxa_rtc_hack); ++ if ( (save_RCNR[0] ^ save_RCNR[1]) == 0xffffffff ) { ++ printk("Restoring saved RCNR value to %ld (from %lx)\n", ++ save_RCNR[0], (unsigned long) save_RCNR); ++ RCNR = save_RCNR[0]; ++ } ++ else { ++ printk("No valid saved RCNR value found at %lx\n", (unsigned long) save_RCNR); ++ } + } ++#endif /* CONFIG_PXA_RTC_HACK */ +--- linux-2.4.21/arch/arm/mach-pxa/generic.h~pm ++++ linux-2.4.21/arch/arm/mach-pxa/generic.h +@@ -17,3 +17,7 @@ + mi->bank[__nr].size = (__size), \ + mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27) + ++#ifdef CONFIG_PXA_RTC_HACK ++void pxa_rtc_hack_init(void); ++extern unsigned long *save_RCNR; ++#endif +--- /dev/null ++++ linux-2.4.21/arch/arm/mach-pxa/pm-common.c +@@ -0,0 +1,285 @@ ++/* ++ * SA1100 Power Management Routines ++ * ++ * Copyright (c) 2001 Cliff Brake ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * History: ++ * ++ * 2001-02-06: Cliff Brake Initial code ++ * ++ * 2001-02-25: Sukjae Cho & ++ * Chester Kuo ++ * Save more value for the resume function! Support ++ * Bitsy/Assabet/Freebird board ++ * ++ * 2001-08-29: Nicolas Pitre ++ * Cleaned up, pushed platform dependent stuff ++ * in the platform specific files. ++ * ++ * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array. ++ * Storage is local on the stack now. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++#ifdef CONFIG_IPAQ_HANDHELD ++#include ++#endif ++ ++#define __KERNEL_SYSCALLS__ ++#include ++ ++ ++ ++static char pm_helper_path[128] = "/etc/apm/apmd_proxy"; ++extern int exec_usermodehelper(char *path, char **argv, char **envp); ++int debug_pm = 0; ++static int pm_helper_veto = 0; ++ ++static int ++run_sbin_pm_helper( pm_request_t action ) ++{ ++ int i; ++ char *argv[3], *envp[8]; ++ ++ if (!pm_helper_path[0]) ++ return 2; ++ ++ if ( action != PM_SUSPEND && action != PM_RESUME ) ++ return 1; ++ ++ /* Be root */ ++ current->uid = current->gid = 0; ++ ++ i = 0; ++ argv[i++] = pm_helper_path; ++ argv[i++] = (action == PM_RESUME ? "resume" : "suspend"); ++ ++ if (action == PM_RESUME) ++ argv[i++]="suspend"; ++ ++ argv[i] = 0; ++ ++ i = 0; ++ /* minimal command environment */ ++ envp[i++] = "HOME=/"; ++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ envp[i] = 0; ++ ++ /* other stuff we want to pass to /sbin/pm_helper */ ++ return exec_usermodehelper (argv [0], argv, envp); ++} ++ ++/* ++ * If pm_suggest_suspend_hook is non-NULL, it is called by pm_suggest_suspend. ++ */ ++int (*pm_suggest_suspend_hook)(int state); ++EXPORT_SYMBOL(pm_suggest_suspend_hook); ++ ++/* ++ * If pm_use_sbin_pm_helper is nonzero, then run_sbin_pm_helper is called before suspend and after resume ++ */ ++int pm_use_sbin_pm_helper = 1; ++EXPORT_SYMBOL(pm_use_sbin_pm_helper); ++ ++/* ++ * If sysctl_pm_do_suspend_hook is non-NULL, it is called by sysctl_pm_do_suspend. ++ * If it returns a true value, then pm_suspend is not called. ++ * Use this to hook in apmd, for now. ++ */ ++int (*pm_sysctl_suspend_hook)(int state); ++EXPORT_SYMBOL(pm_sysctl_suspend_hook); ++ ++int pm_suspend(void); ++ ++int pm_suggest_suspend(void) ++{ ++ int retval; ++ ++ if (pm_suggest_suspend_hook) { ++ if (pm_suggest_suspend_hook(PM_SUSPEND)) ++ return 0; ++ } ++ ++ if (pm_use_sbin_pm_helper) { ++ pid_t pid; ++ int res; ++ int status = 0; ++ unsigned int old_fs; ++ ++ pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_SUSPEND, 0 ); ++ if ( pid < 0 ) ++ return pid; ++ ++ if (debug_pm) ++ printk(KERN_CRIT "%s:%d got pid=%d\n", __FUNCTION__, __LINE__, pid); ++ ++ old_fs = get_fs (); ++ set_fs (get_ds ()); ++ res = waitpid(pid, &status, __WCLONE); ++ set_fs (old_fs); ++ ++ if ( pid != res ) { ++ if (debug_pm) ++ printk(KERN_CRIT ": waitpid returned %d (exit_code=%d); not suspending\n", res, status ); ++ ++ return -1; ++ } ++ ++ /*if ( WIFEXITED(status) && ( WIFEXITSTATUS(status) != 0 )) {*/ ++ if (( status & 0xff7f ) != 0 ) { ++ if (pm_helper_veto) { ++ if (debug_pm) ++ printk(KERN_CRIT "%s: SUSPEND WAS CANCELLED BY pm_helper (exit status %d)\n", __FUNCTION__, status >> 8); ++ return -1; ++ } else { ++ if (debug_pm) ++ printk(KERN_CRIT "%s: pm_helper returned %d, but going ahead anyway\n", __FUNCTION__, status >> 8); ++ } ++ } ++ } ++ ++ if (debug_pm) ++ printk(KERN_CRIT "%s: REALLY SUSPENDING NOW\n", __FUNCTION__ ); ++ ++ if (pm_sysctl_suspend_hook) { ++ if (pm_sysctl_suspend_hook(PM_SUSPEND)) ++ return 0; ++ } ++ ++ retval = pm_suspend(); ++ if (retval) { ++ if (debug_pm) ++ printk(KERN_CRIT "pm_suspend returned %d\n", retval); ++ return retval; ++ } ++ ++ if (pm_use_sbin_pm_helper) { ++ pid_t pid; ++ ++ if (debug_pm) ++ printk(KERN_CRIT "%s: running pm_helper for wakeup\n", __FUNCTION__); ++ ++ pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_RESUME, 0 ); ++ if ( pid < 0 ) ++ return pid; ++ ++ if ( pid != waitpid ( pid, NULL, __WCLONE )) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(pm_suggest_suspend); ++ ++ ++/* ++ * Send us to sleep. ++ */ ++int pm_suspend(void) ++{ ++ int retval; ++ ++ retval = pm_send_all(PM_SUSPEND, (void *)3); ++ if ( retval ) ++ return retval; ++ ++#ifdef CONFIG_IPAQ_HANDHELD ++ retval = h3600_power_management(PM_SUSPEND); ++ if (retval) { ++ pm_send_all(PM_RESUME, (void *)0); ++ return retval; ++ } ++#endif ++ ++ retval = pm_do_suspend(); ++ ++#ifdef CONFIG_IPAQ_HANDHELD ++ /* Allow the power management routines to override resuming */ ++ while ( h3600_power_management(PM_RESUME) ) ++ retval = pm_do_suspend(); ++#endif ++ ++ pm_send_all(PM_RESUME, (void *)0); ++ ++ return retval; ++} ++EXPORT_SYMBOL(pm_suspend); ++ ++#ifdef CONFIG_SYSCTL ++/* ++ * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than ++ * linux/sysctl.h. ++ * ++ * This means our interface here won't survive long - it needs a new ++ * interface. Quick hack to get this working - use sysctl id 9999. ++ */ ++#warning ACPI broke the kernel, this interface needs to be fixed up. ++#define CTL_ACPI 9999 ++#define ACPI_S1_SLP_TYP 19 ++ ++/* ++ * Send us to sleep. ++ */ ++static int sysctl_pm_do_suspend(void) ++{ ++ int retval; ++ ++ retval = pm_send_all(PM_SUSPEND, (void *)3); ++ ++ if (retval == 0) { ++ retval = pm_do_suspend(); ++ ++ pm_send_all(PM_RESUME, (void *)0); ++ } ++ ++ return retval; ++} ++ ++static struct ctl_table pm_table[] = ++{ ++ {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_do_suspend}, ++ {2, "helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring}, ++ {3, "debug", &debug_pm, sizeof(debug_pm), 0644, NULL, (proc_handler *)&proc_dointvec}, ++ {4, "helper_veto", &pm_helper_veto, sizeof(pm_helper_veto), 0644, NULL, (proc_handler *)&proc_dointvec}, ++ {0} ++}; ++ ++static struct ctl_table pm_dir_table[] = ++{ ++ {CTL_ACPI, "pm", NULL, 0, 0555, pm_table}, ++ {0} ++}; ++ ++/* ++ * Initialize power interface ++ */ ++static int __init pm_init(void) ++{ ++ register_sysctl_table(pm_dir_table, 1); ++ return 0; ++} ++ ++__initcall(pm_init); ++ ++#endif ++ +--- linux-2.4.21/arch/arm/mach-pxa/pm.c~pm ++++ linux-2.4.21/arch/arm/mach-pxa/pm.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -82,7 +83,7 @@ + + /* + * Temporary solution. This won't be necessary once +- * we move pxa support into the serial/* driver ++ * we move pxa support into the serial driver + * Save the FF UART + */ + SAVE(FFIER); +@@ -176,7 +177,7 @@ + + /* + * Temporary solution. This won't be necessary once +- * we move pxa support into the serial/* driver. ++ * we move pxa support into the serial driver. + * Restore the FF UART. + */ + RESTORE(FFMCR); +@@ -209,6 +210,12 @@ + return virt_to_phys(sp); + } + ++#ifndef CONFIG_APM ++/* ++ * This code is only needed if we don't compile in APM support. ++ * If we compile APM support in, then this code is in pm-common.c ++ */ ++ + #ifdef CONFIG_SYSCTL + /* + * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than +@@ -263,3 +270,6 @@ + __initcall(pm_init); + + #endif ++#endif ++ ++EXPORT_SYMBOL(pm_do_suspend); +--- linux-2.4.21/arch/arm/mach-pxa/pxa_usb.h~pxa-usb ++++ linux-2.4.21/arch/arm/mach-pxa/pxa_usb.h +@@ -39,6 +39,7 @@ + int pxa_usb_xmitter_avail( void ); + int pxa_usb_send(char *buf, int len, usb_callback_t callback); + void sa110a_usb_send_reset(void); ++void pxa_usb_send_reset(void); + + /* in usb_recev.c */ + int pxa_usb_recv(char *buf, int len, usb_callback_t callback); +--- /dev/null ++++ linux-2.4.21/arch/arm/mach-pxa/ramses.c +@@ -0,0 +1,844 @@ ++/* ++ * linux/arch/arm/mach-pxa/ramses.c ++ * ++ * 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) 2002,2003,2004 by M&N Logistik-Lösungen Online GmbH ++ * written by Holger Schurig ++ * ++ * 2001-09-13: Cliff Brake ++ * Initial code ++ * ++ * 2002-10-09: adaptions to ramses ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_APM ++#include ++#endif ++#define USE_UCB ++//#define PFI_LED ++#define PFI_TURNOFF ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef USE_UCB ++#include "../drivers/misc/ucb1x00.h" ++#endif ++ ++#include "generic.h" ++ ++ ++/* shadow registers for write only registers */ ++u16 ramses_control_shadow = ++ RAMSES_CONTROL_LED_BLUE_ + ++ RAMSES_CONTROL_LED_ORANGE_ + ++ RAMSES_CONTROL_SCANNER_WAKE_ + ++ RAMSES_CONTROL_SCANNER_TRIG_; ++ ++/* various flags the change the behavior of the kernel */ ++unsigned int ramses_flags = ++ RAMSES_FLAGS_KEY_SCAN + ++ RAMSES_FLAGS_KEY_SUSPEND + ++ RAMSES_FLAGS_KEY_OFF; ++ ++ ++/******************************************************************/ ++/* Corevoltage settings */ ++/******************************************************************/ ++ ++int ramses_corevolt_shadow = 150; ++ ++void ramses_set_corevolt(int volt) ++{ ++ int val = 0; ++ switch (volt) { ++ case 150: ++ val = 5; ++ break; ++ case 135: ++ val = 8; ++ break; ++ case 125: ++ val = 10; ++ break; ++ case 115: ++ val = 12; ++ break; ++ case 105: ++ val = 14; ++ break; ++ } ++ if (val) { ++ ramses_corevolt_shadow = volt; ++ RAMSES_COREVOLT = val; ++ RAMSES_CPLD_PERIPH_PWR |= CORE_VAR_EN; ++ } ++} ++ ++ ++/******************************************************************/ ++/* LCD stuff */ ++/******************************************************************/ ++ ++u16 ramses_lcd_type; ++ ++void ramses_lcd_power_on(void) ++{ ++ //printk("--> ramses_lcd_power_on\n"); ++ ++ /* 1. VCC */ ++ RAMSES_CPLD_LCD |= RAMSES_LCD_VCC; ++ ++ /* 2. Signal */ ++ ++ /* 3. the PWM */ ++ CKEN |= (CKEN0_PWM0 | CKEN1_PWM1); ++ ++ /* 4. nDISPOFF (done at backlight_on time) */ ++ RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; ++} ++ ++ ++void ramses_lcd_power_off(void) ++{ ++ //printk("--> ramses_lcd_power_off\n"); ++ if (RAMSES_CPLD_LCD & RAMSES_LCD_DISPOFF) { ++ //printk("--> turn bl off first\n"); ++ ramses_lcd_backlight_off(); ++ } ++ ++ /* 1. nDISPOFF (just to be sure) */ ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; ++ ++ // for Torisan: wait until all has been sent out ++ if (ramses_lcd_type == 2) { ++ int i; ++ for (i=0; i<33; i++) ++ udelay(1500); ++ } ++ ++ /* 2. disable the PWM */ ++ set_GPIO_mode(GPIO16_PWM0 | GPIO_OUT); ++ set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); ++ CKEN &= ~(CKEN0_PWM0 | CKEN1_PWM1); ++ ++ /* 3. SIGNAL */ ++ ++ /* 4. VCC */ ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_VCC; ++} ++ ++ ++void ramses_lcd_backlight_on(void) ++{ ++ int i; ++ ++ //printk("--> ramses_lcd_backlight_on\n"); ++ if ((ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) != 0) ++ return; ++ //printk(" state: %d\n", ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT); ++ ++ set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); ++ ++ /* 4. nDISPOFF */ ++ RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; ++ ++ /* Backlight can be turned on at any time */ ++ RAMSES_LCD_BLIGHT_ON(); ++ ++ for (i=0; i<33; i++) ++ udelay(1500); ++ set_GPIO_mode(GPIO16_PWM0_MD); ++ set_GPIO_mode(GPIO17_PWM1_MD); ++} ++ ++ ++void ramses_lcd_backlight_off(void) ++{ ++ int i; ++ ++ //printk("--> ramses_lcd_backlight_off\n"); ++ if ((ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) == 0) ++ return; ++ //printk(" state: %d\n", ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT); ++ ++ set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); ++ for (i=0; i<100; i++) ++ udelay(1500); ++ ++ /* Backlight can be turned off at any time */ ++ RAMSES_LCD_BLIGHT_OFF(); ++ udelay(1500); ++ ++ /* 1. nDISPOFF */ ++ if (ramses_lcd_type != 2) ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; ++ ++ //set_GPIO_mode(GPIO16_PWM0 | GPIO_IN); ++ //set_GPIO_mode(GPIO17_PWM1 | GPIO_IN); ++} ++ ++ ++void ramses_lcd_set_brightness(int b) ++{ ++ if (b > 255) b = 255; ++ if (b < 0) b = 0; ++ PWM_PWDUTY1 = 510-(b<<1); ++} ++ ++ ++int ramses_lcd_get_brightness(void) ++{ ++ return 255-(PWM_PWDUTY1 >> 1); ++} ++ ++ ++void ramses_lcd_set_contrast(int c) ++{ ++ if (c > 255) c = 255; ++ if (c < 0) c = 0; ++ PWM_PWDUTY0 = 542-c; ++} ++ ++ ++int ramses_lcd_get_contrast(void) ++{ ++ return 542-PWM_PWDUTY0; ++} ++ ++ ++void ramses_lcd_set_intensity(int i) ++{ ++ //printk("--> ramses_lcd_set_intensity(%d)\n", i); ++ if (i) { ++ ramses_lcd_backlight_on(); ++ } else { ++ ramses_lcd_backlight_off(); ++ } ++} ++ ++ ++int ramses_lcd_get_intensity(void) ++{ ++ return ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT; ++} ++ ++ ++ ++ ++ ++/******************************************************************/ ++/* HDQ communication */ ++/******************************************************************/ ++ ++#define GPIO_HDQ 2 ++ ++#define HDQ_LO GPCR(GPIO_HDQ) = GPIO_bit(GPIO_HDQ) ++#define HDQ_HI GPSR(GPIO_HDQ) = GPIO_bit(GPIO_HDQ) ++#define HDQ_GET (GPLR(GPIO_HDQ) & (1 << GPIO_HDQ)) ++ ++#define MAXLOOPS 800 ++#define MAXTRIES 3 ++ ++ ++static void hdq_break(void) ++{ ++ HDQ_LO; ++ set_GPIO_mode(GPIO_HDQ | GPIO_OUT); ++ udelay(220); ++ HDQ_HI; ++ udelay(50); ++} ++ ++ ++/** ++ * Send data on the 1-bit wire. ++ * ++ * LSB first. Depending on the bit, do the low phase short or ++ * small. The used timings in usec's are made so that our send ++ * stuff has exactly the timing that the BQ2050 battery sends ++ * us back. ++*/ ++static void hdq_put_data(unsigned char cmd) ++{ ++ unsigned char mask = 1; ++ ++ HDQ_HI; ++ set_GPIO_mode(GPIO_HDQ | GPIO_OUT); ++ ++ while (1) { ++ HDQ_LO; ++ udelay(cmd & mask ? 37 : 115); ++ HDQ_HI; ++ udelay(cmd & mask ? 163 : 85); ++ if (mask == 0x80) break; ++ mask = mask << 1; ++ } ++ set_GPIO_mode(GPIO_HDQ | GPIO_IN); ++} ++ ++ ++/** ++ * Receive data on the 1-bit wire. ++ * ++ * Little state-machine with two states (yuck) that measures the time ++ * in the low-state. If it exceeds some value, then the bit was a 0, ++ * otherwise it's a 1. ++*/ ++static int hdq_get_data(void) ++{ ++ enum { ST_WAITLOW, ST_LOW }; ++ ++ int i; ++ int lastlow = 0; ++ int state = ST_WAITLOW; ++ unsigned char mask = 1; ++ unsigned char d = 0; ++ ++ for (i=0; i %08x\n", old_shadow, ramses_control_shadow); ++ ++ RAMSES_CPLD_PERIPH_PWR &= ~PER_PWR_EN; ++ break; ++ ++ case PM_RESUME: ++ RAMSES_CPLD_PERIPH_PWR |= PER_PWR_EN; ++ ramses_control_shadow = old_shadow; ++ PWM_CTRL0 = old_ctrl0; ++ PWM_CTRL1 = old_ctrl1; ++ PWM_PERVAL0 = old_perval0; ++ PWM_PERVAL1 = old_perval1; ++ PWM_PWDUTY0 = old_duty0; ++ PWM_PWDUTY1 = old_duty1; ++ ++ break; ++ } ++ return 0; ++} ++ ++#endif ++ ++ ++ ++static void pf_interrupt(int irq, void *dummy, struct pt_regs *fp) ++{ ++#ifdef PFI_LED ++ RAMSES_LED_BLUE_ON(); ++ RAMSES_LED_ORANGE_ON(); ++#endif ++#ifdef PFI_TURNOFF ++ // Make sure we can't be turned on by setting low onto the CPLD's columns ++ RAMSES_CPLD_KB_COL_LOW = 0; ++ RAMSES_CPLD_KB_COL_HIGH = 0; ++ ++ // turn power off ++ RAMSES_POWER_OFF(); ++ ++ // wait until VCC fades ++ while (1) { } ++#endif ++} ++ ++ ++void ramses_shut_off(void) ++{ ++ // Make sure we can't be turned on by setting low onto the CPLD's columns ++ RAMSES_CPLD_KB_COL_LOW = 0; ++ RAMSES_CPLD_KB_COL_HIGH = 0; ++ //printk("--> ramses_shut_off calling ramses_lcd_backlight_off\n"); ++ ramses_lcd_backlight_off(); ++ //printk("--> ramses_shut_off calling ramses_lcd_power_off\n"); ++ ramses_lcd_power_off(); ++ ++ // turn power off ++ RAMSES_POWER_OFF(); ++ ++ // wait until voltage fades ++ while (1) {} ++} ++ ++ ++ ++ ++#ifdef CONFIG_APM ++static int ramses_get_power_status(u_char *ac_line_status, ++ u_char *battery_status, ++ u_char *battery_flag, ++ u_char *battery_percentage, ++ u_short *battery_life) ++{ ++#ifdef USE_UCB ++ int adc3; ++ struct ucb1x00 *ucb = ucb1x00_get(); ++ ++ ucb1x00_adc_enable(ucb); ++ adc3 = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD3, 0); ++ ucb1x00_adc_disable(ucb); ++ ++ /* ++ * when charged: 0..430 ++ * when discharging: 0..340 ++ */ ++ ++#define CHG_LO 165 ++#define CHG_HI 420 ++#define OFF_LO 60 ++#define OFF_HI 350 ++ if ((RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0) { ++ // in Docking-Station ++ if (adc3 > CHG_HI) adc3 = CHG_HI; ++ if (adc3 < CHG_LO) adc3 = CHG_LO; ++ adc3 -= CHG_LO; ++ *battery_percentage = adc3 * 100 / (CHG_HI-CHG_LO); ++ *ac_line_status = 0x01; ++ } else { ++ // offline ++ if (adc3 > OFF_HI) adc3 = OFF_HI; ++ if (adc3 < OFF_LO) adc3 = OFF_LO; ++ adc3 -= OFF_LO; ++ *battery_percentage = adc3 * 100 / (OFF_HI-OFF_LO); ++ *ac_line_status = 0x00; ++ } ++ ++ if (*battery_percentage > 100) ++ *battery_percentage = 100; ++ ++ if (*ac_line_status) { ++ *battery_status = 3; // charging ++ *battery_flag = 1<<3; ++ } else ++ if (*battery_percentage >= 30) { ++ *battery_status = 0; // high ++ *battery_flag = 1<<0; ++ } else ++ if (*battery_percentage >= 15) { ++ *battery_status = 1; // low ++ *battery_flag = 1<<1; ++ } else { ++ *battery_status = 2; // critical ++ *battery_flag = 1<<2; ++ } ++ ++ // assume 5.5 hours operation, 330 minutes ++ *battery_life = (*battery_percentage * 330 / 100) | 0x8000; ++#endif ++ ++ ++#ifdef USE_HDQ ++#error HDQ ++ // SAE is something like mAh * 10 ++ int sae, sael; ++ ++ sael = ramses_hdq_get_reg(HDQ_SAEL); ++ sae = ramses_hdq_get_reg(HDQ_SAEH); ++ ++ if (sae == -1 || sael == -1) { ++ //printk("ramses: could not read HDQ_SAE\n"); ++ *ac_line_status = 0xff; ++ *battery_status = 0xff; ++ *battery_flag = 0xff; ++ *battery_percentage = 0xff; ++ *battery_life = -1; ++ return 0; ++ } ++ ++ sae = (sae << 16) + sael; ++ if (sae > 27000) { ++ printk("ramses: capped HDQ_SAE from %d to 27000\n", sae); ++ sae = 27000; ++ } ++ ++ if (sae < 4000) { ++ *battery_status = 2; // critical ++ *battery_flag = 1<<2; ++ } else ++ if (sae < 10000) { ++ *battery_status = 1; // low ++ *battery_flag = 1<<1; ++ } else { ++ *battery_status = 0; // high ++ *battery_flag = 1<<0; ++ } ++ ++ if ((RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0) { ++ *battery_status = 3; // charging ++ *battery_flag = 1<<3; ++ *ac_line_status = 0x01; // online ++ } else { ++ *ac_line_status = 0x00; // offline ++ } ++ ++ *battery_percentage = sae / 270; ++ *battery_life = (sae / 56) | 0x8000; ++#endif ++ ++ ++#if !defined(USE_UCB) && !defined(USE_HDQ) ++#error NONE ++ *ac_line_status = 0xff; ++ *battery_status = 0xff; ++ *battery_flag = 0xff; ++ *battery_percentage = 0xff; ++ *battery_life = -1; ++#endif ++ ++ return 0; ++} ++#endif ++ ++ ++ ++ ++/******************************************************************/ ++/* Initialisation */ ++/******************************************************************/ ++ ++static struct map_desc ramses_io_desc[] __initdata = { ++ /* virtual physical length domain r w c b */ ++ { RAMSES_IDE_BASE, RAMSES_IDE_PHYS, RAMSES_IDE_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, ++ { RAMSES_ETH_BASE, RAMSES_ETH_PHYS, RAMSES_ETH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, ++ { RAMSES_COREVOLT_BASE, RAMSES_COREVOLT_PHYS, RAMSES_COREVOLT_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, ++ { RAMSES_CPLD_BASE, RAMSES_CPLD_PHYS, RAMSES_CPLD_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, ++ { RAMSES_CONTROL_BASE, RAMSES_CONTROL_PHYS, RAMSES_CONTROL_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, ++ LAST_DESC ++}; ++ ++ ++ ++ ++ ++/* ++ * Uncompressing Linux...... done, booting the kernel. ++ * Linux version 2.4.19-rmk4-pxa1-mn ... ++ * CPU: Intel XScale-PXA250 revision 4 ++ * Machine: Ramses ++ * fixup_ramses() ++ */ ++ ++static void __init ++fixup_ramses(struct machine_desc *desc, struct param_struct *params, ++ char **cmdline, struct meminfo *mi) ++{ ++ SET_BANK (0, 0xa0000000,128*1024*1024); ++ mi->nr_banks = 1; ++} ++ ++ ++ ++ ++/* ++ * fixup_ramses() ++ * ramses_map_io() ++ */ ++ ++static void __init ramses_map_io(void) ++{ ++ u16 smc_eeprom_read(long ioaddr, u16 location); ++ ++ pxa_map_io(); ++ iotable_init(ramses_io_desc); ++ ++#ifdef IPAQ ++ // set power managament stuff ++ PGSR0 = GPSRx_SleepValue; ++ PGSR1 = GPSRy_SleepValue; ++ PGSR2 = GPSRz_SleepValue; ++ PWER = PWER_GPIO0 | PWER_RTC; ++ PFER = PWER_GPIO0 | PWER_RTC; ++ PRER = 0; ++#endif ++ ++ ramses_lcd_type = smc_eeprom_read(RAMSES_ETH_BASE+0x300, RAMSES_LCD_TYPE_OFFSET); ++ // here we could make a special case about ramses_lcd_type == 0xffff ++ ramses_flags |= (ramses_lcd_type & RAMSES_FLAGS_LCD_FBTURN); ++} ++ ++ ++ ++ ++/* ++ * ramses_map_io() ++ * Memory clock: 99.53MHz (*27) ++ * Run Mode clock: 199.07MHz (*2) ++ * Turbo Mode clock: 398.13MHz (*2.0, active) * On node 0 totalpages: 16384 ++ * zone(0): 32768 pages. ++ * zone(1): 0 pages. ++ * zone(2): 0 pages. ++ * Kernel command line: root=/dev/nfsroot ... ++ * ramses_init_irq() ++ */ ++ ++static void __init ramses_init_irq(void) ++{ ++ set_GPIO_IRQ_edge(21, GPIO_FALLING_EDGE); // UCB 1400 ++ ++ RAMSES_SCANNER_OFF(); ++ RAMSES_GSM_OFF(); ++ RAMSES_GSM_RESET_OFF(); ++ RAMSES_GSM_BOOT_OFF(); ++ pxa_init_irq(); ++} ++ ++ ++ ++ ++/* ++ * ramses_init_irq() ++ * Console: colour dummy device 80x30 ++ * serial_console_init ++ * serial_console_setup ++ * Calibrating delay loop... 397.31 BogoMIPS ++ * Memory: 128MB = 128MB total ++ * Memory: 127872KB available (1355K code, 272K data, 112K init) ++ * Dentry cache hash table entries: 16384 (order: 5, 131072 bytes) ++ * Inode cache hash table entries: 8192 (order: 4, 65536 bytes) ++ * Mount-cache hash table entries: 2048 (order: 2, 16384 bytes) ++ * Buffer-cache hash table entries: 8192 (order: 3, 32768 bytes) ++ * Page-cache hash table entries: 32768 (order: 5, 131072 bytes) ++ * POSIX conformance testing by UNIFIX ++ * Linux NET4.0 for Linux 2.4 ++ * Based upon Swansea University Computer Society NET3.039 ++ * Initializing RT netlink socket ++ * ramses_init() ++ */ ++ ++static int __init ramses_init(void) ++{ ++ unsigned int irq_gpio_pin; ++ ++ // Set IRQ for Touchpanel (via UCB 1400) ++ irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ); ++ set_GPIO_IRQ_edge(irq_gpio_pin, TOUCH_PANEL_IRQ_EDGE); ++ ++ // Set IRQ for Power Fail Interrupt ++ set_GPIO_IRQ_edge(1, GPIO_FALLING_EDGE); ++ request_irq(IRQ_GPIO(1), pf_interrupt, 0, "PWR FAIL", NULL); ++ ++ // Setup IRQ edge for Ethernet ++ //set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(27), GPIO_RISING_EDGE); ++ set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(ETHERNET_IRQ), ETHERNET_IRQ_EDGE); ++ ++ // Configure PWMs for LCD ++ PWM_CTRL0 = 0; ++ PWM_PERVAL0 = 512; ++ PWM_PWDUTY0 = 440; ++ PWM_CTRL1 = 0; ++ PWM_PERVAL1 = 512; ++ PWM_PWDUTY1 = 450; ++ ++ // Request Memory Regions of core components ++ request_mem_region(RAMSES_CONTROL_BASE, RAMSES_CONTROL_SIZE, "Ramses Control"); ++ request_mem_region(RAMSES_CPLD_BASE, RAMSES_CPLD_SIZE, "Ramses CPLD"); ++ request_mem_region(RAMSES_COREVOLT_BASE, RAMSES_COREVOLT_SIZE, "Ramses Corevolt"); ++ ++#ifdef CONFIG_PM ++#ifdef PM_DEBUG ++ pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ramses_pm_callback, "ramses"); ++#else ++ pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ramses_pm_callback); ++#endif ++ //TODO PWER (PXA255: 3-25) ++ //TODO PRER (PXA255: 3-26) ++ //TODO PFER (PXA255: 3-27) ++ //TODO PSSR (PXA255: 3-29) ++ //TODO PGSR0..PGSR2 (PXA255: 3-32) ++#endif ++ ++#ifdef CONFIG_APM ++ apm_register_get_power_status(ramses_get_power_status); ++#endif ++ ++ PCFR = PCFR_OPDE | PCFR_FS | PCFR_FP; // PXA255: 3-24 ++ ++ pm_power_off = ramses_shut_off; ++ ++ return 0; ++} ++ ++__initcall(ramses_init); ++ ++ ++ ++/* ++ * ramses_init() ++ * Using PXA255 frequency points. ++ * Registering CPU frequency change support. ++ * CPU clock: 398.131 MHz (99.000-400.000 MHz) ++ * Starting kswapd ++ * devfs: v1.12a (20020514) Richard Gooch (rgooch@atnf.csiro.au) ++ * devfs: boot_options: 0x1 ++ * pty: 256 Unix98 ptys configured ++ * pxa & ti16c754b serial driver ++ * tts/0 at irq 14 is a PXA UART ++ * tts/1 at irq 13 is a PXA UART ++ * tts/2 at irq 12 is a PXA UART ++ * tts/3 at irq 45 is a TI16750 ++ * tts/4 at irq 46 is a TI16750 ++ * tts/5 at irq 47 is a TI16750 ++ * tts/6 at irq 48 is a TI16750 ++ * LAN91C111: You shouldn't use auto-probing with insmod! ++ * SMSC LAN91C111 Driver (v2.2), (Linux Kernel 2.4 + Support for Odd Byte) ... ++ * eth0: SMC91C11xFD(rev:1) at 0xf0100300 IRQ:26 MEMSIZE ... ++ * ac97_codec: AC97 Audio codec, id: 0x5053:0x4304 (Philips UCB1400) ++ * NET4: Linux TCP/IP 1.0 for NET4.0 ++ * IP Protocols: ICMP, UDP, TCP ++ * IP: routing cache hash table of 512 buckets, 4Kbytes ++ * TCP: Hash tables configured (established 4096 bind 4096) ++ * IP-Config: ... ++ * NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. ++ * NetWinder Floating Point Emulator V0.95 (c) 1998-1999 Rebel.com ++ * Looking up port of RPC 100003/2 on 192.168.233.66 ++ * Looking up port of RPC 100005/1 on 192.168.233.66 ++ * VFS: Mounted root (nfs filesystem). ++ * Mounted devfs on /dev ++ * Freeing init memory: 68K ++ * INIT: version 2.84 booting ++ */ ++ ++ ++ ++MACHINE_START(RAMSES, "Ramses") ++ MAINTAINER("M&N Logistik-Lösungen Online GmbH") ++ BOOT_MEM(0xa0000000, 0x40000000, 0xfc000000) ++ BOOT_PARAMS(0xa0000100) ++ FIXUP(fixup_ramses) ++ MAPIO(ramses_map_io) ++ INITIRQ(ramses_init_irq) ++MACHINE_END ++ ++EXPORT_SYMBOL(ramses_lcd_type); ++EXPORT_SYMBOL(ramses_lcd_power_on); ++EXPORT_SYMBOL(ramses_lcd_power_off); ++EXPORT_SYMBOL(ramses_lcd_backlight_on); ++EXPORT_SYMBOL(ramses_lcd_backlight_off); ++EXPORT_SYMBOL(ramses_lcd_set_intensity); ++EXPORT_SYMBOL(ramses_lcd_set_brightness); ++EXPORT_SYMBOL(ramses_lcd_set_contrast); ++EXPORT_SYMBOL(ramses_lcd_get_intensity); ++EXPORT_SYMBOL(ramses_lcd_get_brightness); ++EXPORT_SYMBOL(ramses_lcd_get_contrast); ++EXPORT_SYMBOL(ramses_hdq_get_reg); ++EXPORT_SYMBOL(ramses_set_corevolt); ++EXPORT_SYMBOL(ramses_corevolt_shadow); +--- linux-2.4.21/arch/arm/mach-pxa/usb-char.c~pxa-usb ++++ linux-2.4.21/arch/arm/mach-pxa/usb-char.c +@@ -211,7 +211,6 @@ + static void twiddle_descriptors( void ) + { + desc_t * pDesc = pxa_usb_get_descriptor_ptr(); +- string_desc_t * pString; + + pDesc->b.ep1.wMaxPacketSize = make_word_c( RX_PACKET_SIZE ); + pDesc->b.ep1.bmAttributes = USB_EP_BULK; +@@ -220,6 +219,7 @@ + + if ( machine_is_extenex1() ) { + #ifdef CONFIG_SA1100_EXTENEX1 ++ string_desc_t * pString; + pDesc->dev.idVendor = make_word_c( 0xC9F ); + pDesc->dev.idProduct = 1; + pDesc->dev.bcdDevice = make_word_c( 0x0001 ); +--- linux-2.4.21/arch/arm/mach-pxa/usb-eth.c~pxa-usbeth ++++ linux-2.4.21/arch/arm/mach-pxa/usb-eth.c +@@ -52,6 +52,7 @@ + + #define ETHERNET_VENDOR_ID 0x49f + #define ETHERNET_PRODUCT_ID 0x505A ++#define ETHERNET_DEVICE_ID 0x0200 + #define MAX_PACKET 32768 + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +@@ -329,6 +330,7 @@ + pd->b.ep2.wMaxPacketSize = make_word( usb_wsize ); + pd->dev.idVendor = ETHERNET_VENDOR_ID; + pd->dev.idProduct = ETHERNET_PRODUCT_ID; ++ pd->dev.bcdDevice = ETHERNET_DEVICE_ID; + pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" ); + if ( pstr ) { + pxa_usb_set_string_descriptor( 1, pstr ); +--- linux-2.4.21/drivers/char/Config.in~i2c-ds1337 ++++ linux-2.4.21/drivers/char/Config.in +@@ -164,6 +164,7 @@ + + if [ "$CONFIG_I2C" != "n" ]; then + dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C ++ dep_tristate ' DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C + fi + + source drivers/l3/Config.in +--- linux-2.4.21/drivers/char/Makefile~i2c-ds1337 ++++ linux-2.4.21/drivers/char/Makefile +@@ -21,10 +21,11 @@ + # All of the (potential) objects that export symbols. + # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. + +-export-objs := busmouse.o console.o keyboard.o sysrq.o \ ++export-objs := vt.o busmouse.o console.o keyboard.o sysrq.o \ + misc.o pty.o random.o selection.o serial.o \ + sonypi.o tty_io.o tty_ioctl.o generic_serial.o \ +- au1000_gpio.o hp_psaux.o nvram.o scx200.o ++ au1000_gpio.o hp_psaux.o nvram.o scx200.o \ ++ input_keyb.o + + mod-subdirs := joystick ftape drm drm-4.0 pcmcia + +@@ -129,6 +130,11 @@ + ifeq ($(CONFIG_SA1100_CERF_CPLD),y) + KEYBD += cerf_keyb.o + endif ++ ifeq ($(CONFIG_ARCH_RAMSES),y) ++ KEYMAP = german.o ++ KEYBD += input_keyb.o ++ obj-m += sysctl.o ++ endif + ifeq ($(CONFIG_ARCH_FORTUNET),y) + KEYMAP := defkeymap.o + endif +@@ -337,6 +343,7 @@ + + # I2C char devices + obj-$(CONFIG_I2C_DS1307) += ds1307.o ++obj-$(CONFIG_I2C_DS1337) += ds1337.o + + subdir-$(CONFIG_MWAVE) += mwave + ifeq ($(CONFIG_MWAVE),y) +@@ -373,3 +380,6 @@ + + qtronixmap.c: qtronixmap.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ ++ ++german.c: german.map ++ set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ +--- linux-2.4.21/drivers/char/console.c~keyb-module ++++ linux-2.4.21/drivers/char/console.c +@@ -150,7 +150,7 @@ + static int con_open(struct tty_struct *, struct file *); + static void vc_init(unsigned int console, unsigned int rows, + unsigned int cols, int do_clear); +-static void blank_screen(unsigned long dummy); ++//static void blank_screen(unsigned long dummy); + static void gotoxy(int currcons, int new_x, int new_y); + static void save_cur(int currcons); + static void reset_terminal(int currcons, int do_clear); +@@ -158,7 +158,7 @@ + static void set_vesa_blanking(unsigned long arg); + static void set_cursor(int currcons); + static void hide_cursor(int currcons); +-static void unblank_screen_t(unsigned long dummy); ++//static void unblank_screen_t(unsigned long dummy); + static void console_callback(void *ignored); + + static int printable; /* Is console ready for printing? */ +@@ -167,7 +167,7 @@ + int console_blanked; + + static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ +-static int blankinterval = 10*60*HZ; ++//static int blankinterval = 10*60*HZ; + static int vesa_off_interval; + + static struct tq_struct console_callback_tq = { +@@ -209,9 +209,9 @@ + * Hook so that the power management routines can (un)blank + * the console on our behalf. + */ +-int (*console_blank_hook)(int); ++//int (*console_blank_hook)(int); + +-static struct timer_list console_timer; ++//static struct timer_list console_timer; + + /* + * Low-Level Functions +@@ -543,7 +543,7 @@ + + static void set_cursor(int currcons) + { +- if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS) ++ if (!IS_FG || vcmode == KD_GRAPHICS) + return; + if (deccm) { + if (currcons == sel_cons) +@@ -1287,7 +1287,7 @@ + update_attr(currcons); + break; + case 9: /* set blanking interval */ +- blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; ++ //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; + poke_blanked_console(); + break; + case 10: /* set bell frequency in Hz */ +@@ -2575,11 +2575,11 @@ + if (tty_register_driver(&console_driver)) + panic("Couldn't register console driver\n"); + +- init_timer(&console_timer); +- console_timer.function = blank_screen; +- if (blankinterval) { +- mod_timer(&console_timer, jiffies + blankinterval); +- } ++ //init_timer(&console_timer); ++ //console_timer.function = blank_screen; ++ //if (blankinterval) { ++ // mod_timer(&console_timer, jiffies + blankinterval); ++ //} + + /* + * kmalloc is not running yet - we use the bootmem allocator. +@@ -2744,11 +2744,12 @@ + */ + static void vesa_powerdown_screen(unsigned long dummy) + { +- console_timer.function = unblank_screen_t; ++ //console_timer.function = unblank_screen_t; + + vesa_powerdown(); + } + ++#if 0 + static void timer_do_blank_screen(int entering_gfx, int from_timer_handler) + { + int currcons = fg_console; +@@ -2797,12 +2798,14 @@ + if (vesa_blank_mode) + sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); + } ++#endif + + void do_blank_screen(int entering_gfx) + { +- timer_do_blank_screen(entering_gfx, 0); ++ //timer_do_blank_screen(entering_gfx, 0); + } + ++#if 0 + /* + * This is a timer handler + */ +@@ -2810,12 +2813,14 @@ + { + unblank_screen(); + } ++#endif + + /* + * Called by timer as well as from vt_console_driver + */ + void unblank_screen(void) + { ++#if 0 + int currcons; + + if (!console_blanked) +@@ -2842,6 +2847,7 @@ + /* Low-level driver cannot restore -> do it ourselves */ + update_screen(fg_console); + set_cursor(fg_console); ++#endif + } + + /* +@@ -2849,11 +2855,12 @@ + */ + static void blank_screen(unsigned long dummy) + { +- timer_do_blank_screen(0, 1); ++ //timer_do_blank_screen(0, 1); + } + + void poke_blanked_console(void) + { ++#if 0 + del_timer(&console_timer); + if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS) + return; +@@ -2863,6 +2870,7 @@ + } else if (blankinterval) { + mod_timer(&console_timer, jiffies + blankinterval); + } ++#endif + } + + /* +@@ -3088,7 +3096,7 @@ + unblank_screen(); + break; + case PM_SUSPEND: +- do_blank_screen(0); ++ //do_blank_screen(0); + break; + } + return 0; +@@ -3106,7 +3114,8 @@ + EXPORT_SYMBOL(video_scan_lines); + EXPORT_SYMBOL(vc_resize); + EXPORT_SYMBOL(fg_console); +-EXPORT_SYMBOL(console_blank_hook); ++//EXPORT_SYMBOL(console_blank_hook); ++EXPORT_SYMBOL(console_driver); + #ifdef CONFIG_VT + EXPORT_SYMBOL(vt_cons); + #endif +--- /dev/null ++++ linux-2.4.21/drivers/char/ds1337.c +@@ -0,0 +1,545 @@ ++/* ++ * ds1337.c ++ * ++ * Device driver for Dallas Semiconductor's Real Time Controller DS1337. ++ * ++ * Copyright (C) 2003 M&N Logistik-Lösungen Online 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. ++ * ++ * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.pdf ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ds1337.h" ++ ++//#define DEBUG 1 ++ ++#if DEBUG ++static unsigned int rtc_debug = DEBUG; ++#else ++#define rtc_debug 0 /* gcc will remove all the debug code for us */ ++#endif ++ ++static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR; ++struct i2c_driver ds1337_driver; ++struct i2c_client *ds1337_i2c_client = 0; ++static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED; ++ ++static unsigned short ignore[] = { I2C_CLIENT_END }; ++static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END }; ++ ++static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long); ++static int ds1337_rtc_noop(struct inode *inode, struct file *file); ++ ++static int ds1337_probe(struct i2c_adapter *adap); ++static int ds1337_detach(struct i2c_client *client); ++static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg); ++ ++ ++static struct i2c_client_address_data addr_data = { ++ .normal_i2c = normal_addr, ++ .normal_i2c_range = ignore, ++ .probe = ignore, ++ .probe_range = ignore, ++ .ignore = ignore, ++ .ignore_range = ignore, ++ .force = ignore, ++}; ++ ++static struct file_operations rtc_fops = { ++ .owner = THIS_MODULE, ++ .ioctl = ds1337_rtc_ioctl, ++ .open = ds1337_rtc_noop, ++ .release = ds1337_rtc_noop, ++}; ++ ++static struct miscdevice ds1337_rtc_miscdev = { ++ RTC_MINOR, ++ "rtc", ++ &rtc_fops ++}; ++ ++ ++struct i2c_driver ds1337_driver = { ++ .name = "DS1337", ++ .id = I2C_DRIVERID_DS1337, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = ds1337_probe, ++ .detach_client = ds1337_detach, ++ .command = ds1337_command ++}; ++ ++#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */ ++ ++ ++static int ds1337_readram(char *buf, int len) ++{ ++ unsigned long flags; ++ unsigned char ad[1] = { 0 }; ++ int ret; ++ struct i2c_msg msgs[2] = { ++ {ds1337_i2c_client->addr, 0, 1, ad}, ++ {ds1337_i2c_client->addr, I2C_M_RD, len, buf} ++ }; ++ ++ spin_lock_irqsave(&ds1337_rtc_lock, flags); ++ ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2); ++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags); ++ ++ return ret; ++} ++ ++ ++static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val) ++{ ++ unsigned char buf[2]; ++ buf[0] = reg; ++ buf[1] = val; ++ i2c_master_send(c, (char *) buf, 2); ++} ++ ++static int ds1337_attach(struct i2c_adapter *adap, int addr, ++ unsigned short flags, int kind) ++{ ++ struct i2c_client *c; ++ unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 }; ++ struct i2c_msg msgs[2] = { ++ {addr, 0, 1, ad}, ++ {addr, I2C_M_RD, 1, buf} ++ }; ++ int ret; ++ ++ if (rtc_debug>1) ++ printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind); ++ ++ c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ strcpy(c->name, "DS1337"); ++ c->id = ds1337_driver.id; ++ c->flags = 0; ++ c->addr = addr; ++ c->adapter = adap; ++ c->driver = &ds1337_driver; ++ c->data = NULL; ++ ++ ret = i2c_transfer(c->adapter, msgs, 2); ++ ++ if (ret == 2) { ++ DAT(c) = buf[0]; ++ } else ++ printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret); ++ ++ ds1337_i2c_client = c; ++ ++ ds1337_readram(buf, DS1337_MEM_SIZE); ++ ++ // set 24 hour mode ++ ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24); ++ // INTCN sets INTB to alarm2 (disables SQW) ++ ds1337_setreg(c, 0x5, buf[5] & 0x7f); // clear century ++ ds1337_setreg(c, 0x7, 0x00); // clear Alarm 1 seconds ++ ds1337_setreg(c, 0x8, 0x00); // clear Alarm 1 minutes ++ ds1337_setreg(c, 0x9, 0x40); // clear Alarm 1 hours, 24 hour on ++ ds1337_setreg(c, 0xA, 0x00); // clear Alarm 1 date ++ ds1337_setreg(c, 0xB, 0x00); // clear Alarm 2 minutes ++ ds1337_setreg(c, 0xC, 0x40); // clear Alarm 2 hours, 24 hour on ++ ds1337_setreg(c, 0xD, 0x00); // clear Alarm 2 date ++ ds1337_setreg(c, 0xe, 4); // nEOSC enabled ++ ds1337_setreg(c, 0xf, 0); // clear OSF, A2F, A1F ++ ++ return i2c_attach_client(c); ++} ++ ++ ++static int ds1337_probe(struct i2c_adapter *adap) ++{ ++ if (rtc_debug>1) ++ printk("%s()\n", __FUNCTION__); ++ ++ return i2c_probe(adap, &addr_data, ds1337_attach); ++} ++ ++ ++static int ds1337_detach(struct i2c_client *client) ++{ ++ if (rtc_debug>1) ++ printk("%s()\n", __FUNCTION__); ++ ++ i2c_detach_client(client); ++ ++ return 0; ++} ++ ++ ++static void ds1337_convert_to_time(struct rtc_time *dt, char *buf) ++{ ++ if (rtc_debug>1) ++ printk("%s()\n", __FUNCTION__); ++ ++ dt->tm_sec = BCD_TO_BIN(buf[0]); ++ dt->tm_min = BCD_TO_BIN(buf[1]); ++ dt->tm_hour = DS1337_HOURS_24(buf[2]); ++ ++ dt->tm_mday = BCD_TO_BIN(buf[4]); ++ /* dt->tm_mon is zero-based */ ++ dt->tm_mon = BCD_TO_BIN(buf[5]) - 1; ++ /* year is 1900 + dt->tm_year */ ++ dt->tm_year = BCD_TO_BIN(buf[6]) + 100; ++ ++ if (rtc_debug > 2) { ++ printk("ds1337_get_datetime: year = %d\n", dt->tm_year); ++ printk("ds1337_get_datetime: mon = %d\n", dt->tm_mon); ++ printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday); ++ printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour); ++ printk("ds1337_get_datetime: min = %d\n", dt->tm_min); ++ printk("ds1337_get_datetime: sec = %d\n", dt->tm_sec); ++ } ++} ++ ++ ++static int ds1337_get_datetime(struct i2c_client *client, ++ struct rtc_time *dt) ++{ ++ unsigned char buf[7], addr[1] = { 0 }; ++ struct i2c_msg msgs[2] = { ++ {client->addr, 0, 1, addr}, ++ {client->addr, I2C_M_RD, 7, buf} ++ }; ++ int ret = -EIO; ++ ++ if (rtc_debug) ++ printk("%s()\n", __FUNCTION__); ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ ret = i2c_transfer(client->adapter, msgs, 2); ++ ++ if (ret == 2) { ++ ds1337_convert_to_time(dt, buf); ++ ret = 0; ++ } else ++ printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret); ++ ++ return ret; ++} ++ ++ ++static int ds1337_set_datetime(struct i2c_client *client, ++ struct rtc_time *dt, int datetoo) ++{ ++ unsigned char buf[8]; ++ int ret, len = 4; ++ ++ if (rtc_debug) ++ printk("%s()\n", __FUNCTION__); ++ ++ if (rtc_debug > 2) { ++ printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year); ++ printk("ds1337_set_datetime: tm_mon = %d\n", dt->tm_mon); ++ printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday); ++ printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour); ++ printk("ds1337_set_datetime: tm_min = %d\n", dt->tm_min); ++ printk("ds1337_set_datetime: tm_sec = %d\n", dt->tm_sec); ++ } ++ ++ buf[0] = 0; /* register address on DS1337 */ ++ buf[1] = (BIN_TO_BCD(dt->tm_sec)); ++ buf[2] = (BIN_TO_BCD(dt->tm_min)); ++ buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24; ++ ++ if (datetoo) { ++ len = 8; ++ /* we skip buf[4] as we don't use day-of-week. */ ++ buf[5] = (BIN_TO_BCD(dt->tm_mday)); ++ buf[6] = (BIN_TO_BCD(dt->tm_mon + 1)); ++ /* The year only ranges from 0-99, we are being passed an offset from 1900, ++ * and the chip calulates leap years based on 2000, thus we adjust by 100. ++ */ ++ buf[7] = (BIN_TO_BCD(dt->tm_year - 100)); ++ } ++ ret = i2c_master_send(client, (char *) buf, len); ++ if (ret == len) ++ ret = 0; ++ else ++ printk("ds1337_set_datetime(), i2c_master_send() returned %d\n", ++ ret); ++ ++ ++ return ret; ++} ++ ++ ++#if 0 ++static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl) ++{ ++ *ctrl = DAT(client); ++ ++ if (rtc_debug) ++ printk("%s():%d\n", __FUNCTION__, *ctrl); ++ ++ return 0; ++} ++ ++ ++static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo) ++{ ++ unsigned char buf[2]; ++ int ret; ++ ++ if (rtc_debug) ++ printk("%s(%d)\n", __FUNCTION__, *cinfo); ++ ++ buf[0] = 7; /* control register address on DS1337 */ ++ buf[1] = *cinfo; ++ /* save the control reg info in the client data field so that get_ctrl ++ * function doesn't have to do an I2C transfer to get it. ++ */ ++ DAT(client) = buf[1]; ++ ++ ret = i2c_master_send(client, (char *) buf, 2); ++ ++ return ret; ++} ++#endif ++ ++ ++static int ds1337_command(struct i2c_client *client, unsigned int cmd, ++ void *arg) ++{ ++ if (rtc_debug) ++ printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd); ++ ++ switch (cmd) { ++ case DS1337_GETDATETIME: ++ return ds1337_get_datetime(client, arg); ++ ++ case DS1337_SETTIME: ++ return ds1337_set_datetime(client, arg, 0); ++ ++ case DS1337_SETDATETIME: ++ return ds1337_set_datetime(client, arg, 1); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++ ++static int ds1337_rtc_noop(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++ ++static int ds1337_rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ unsigned long flags; ++ struct rtc_time wtime; ++ int status = 0; ++ ++ if (rtc_debug) ++ printk("%s()\n", __FUNCTION__); ++ ++ switch (cmd) { ++ default: ++ case RTC_UIE_ON: // mask ints from RTC updates ++ case RTC_UIE_OFF: ++ case RTC_PIE_ON: // allow periodic interrupts ++ case RTC_PIE_OFF: ++ case RTC_AIE_ON: // mask alarm int enable bit ++ case RTC_AIE_OFF: ++ case RTC_ALM_SET: ++ /* ++ * This expects a struct rtc_time. Writing 0xff means ++ * "don't care" or "match all". Only the tm_hour, ++ * tm_min and tm_sec are used. ++ */ ++ case RTC_ALM_READ: ++ // get_rtc_alm_time(&wtime); ++ case RTC_IRQP_READ: // Read the periodic IRQ rate ++ case RTC_IRQP_SET: // Set periodic IRQ rate ++ case RTC_EPOCH_READ: ++ // return put_user (epoch, (unsigned long *)arg); ++ case RTC_EPOCH_SET: ++ case RTC_WKALM_SET: ++ case RTC_WKALM_RD: ++ status = -EINVAL; ++ break; ++ ++ case RTC_RD_TIME: ++ spin_lock_irqsave(&ds1337_rtc_lock, flags); ++ ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime); ++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags); ++ ++ if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time))) ++ status = -EFAULT; ++ break; ++ ++ case RTC_SET_TIME: ++ if (!capable(CAP_SYS_TIME)) { ++ status = -EACCES; ++ break; ++ } ++ ++ if (copy_from_user ++ (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) { ++ status = -EFAULT; ++ break; ++ } ++ ++ spin_lock_irqsave(&ds1337_rtc_lock, flags); ++ ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime); ++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags); ++ break; ++ } ++ ++ return status; ++} ++ ++ ++static char *ds1337_mon2str(unsigned int mon) ++{ ++ char *mon2str[12] = { ++ "Jan", "Feb", "Mar", "Apr", "May", "Jun", ++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ++ }; ++ if (mon > 11) ++ return "error"; ++ else ++ return mon2str[mon]; ++} ++ ++ ++static int ds1337_rtc_proc_output(char *buf) ++{ ++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no") ++ ++ unsigned char ram[DS1337_MEM_SIZE]; ++ int ret; ++ ++ char *p = buf; ++ ++ ret = ds1337_readram(ram, DS1337_MEM_SIZE); ++ if (ret > 0) { ++#ifdef DEBUG ++ int i; ++ char text[9]; ++#endif ++ struct rtc_time dt; ++ ++ p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n"); ++ ++ ds1337_convert_to_time(&dt, ram); ++ p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n", ++ dt.tm_mday, ds1337_mon2str(dt.tm_mon), ++ dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec); ++ ++#ifdef DEBUG ++ p += sprintf(p, "RAM dump:\n"); ++ text[8] = '\0'; ++ for (i = 0; i < DS1337_MEM_SIZE; i++) { ++ if ((i % 8) == 0) ++ p += sprintf(p, "%02X: ", i); ++ p += sprintf(p, "%02X ", ram[i]); ++ ++ if ((ram[i] < 32) || (ram[i] > 126)) ++ ram[i] = '.'; ++ text[i % 8] = ram[i]; ++ if ((i % 8) == 7) ++ p += sprintf(p, "%s\n", text); ++ } ++ p += sprintf(p, "\n"); ++#endif ++ } else { ++ p += sprintf(p, "Failed to read RTC memory!\n"); ++ } ++ ++ return p - buf; ++} ++ ++ ++static int ds1337_rtc_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = ds1337_rtc_proc_output(page); ++ ++ if (len <= off + count) ++ *eof = 1; ++ *start = page + off; ++ len -= off; ++ if (len > count) ++ len = count; ++ if (len < 0) ++ len = 0; ++ return len; ++} ++ ++ ++static __init int ds1337_init(void) ++{ ++ int retval = 0; ++ ++ if (rtc_debug>1) ++ printk("%s()\n", __FUNCTION__); ++ ++ if (slave_address != 0xffff) { ++ normal_addr[0] = slave_address; ++ } ++ ++ if (normal_addr[0] == 0xffff) { ++ printk(KERN_ERR ++ "I2C: Invalid slave address for DS1337 RTC (%#x)\n", ++ normal_addr[0]); ++ return -EINVAL; ++ } ++ ++ retval = i2c_add_driver(&ds1337_driver); ++ ++ if (retval == 0) { ++ misc_register(&ds1337_rtc_miscdev); ++ create_proc_read_entry(DS1337_PROC_NAME, 0, 0, ++ ds1337_rtc_read_proc, NULL); ++ printk("I2C: DS1337 RTC driver loaded\n"); ++ } ++ return retval; ++} ++ ++ ++static __exit void ds1337_exit(void) ++{ ++ if (rtc_debug>1) ++ printk("%s()\n", __FUNCTION__); ++ ++ remove_proc_entry(DS1337_PROC_NAME, NULL); ++ misc_deregister(&ds1337_rtc_miscdev); ++ i2c_del_driver(&ds1337_driver); ++} ++ ++ ++module_init(ds1337_init); ++module_exit(ds1337_exit); ++ ++MODULE_PARM(slave_address, "i"); ++MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC"); ++ ++MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ linux-2.4.21/drivers/char/ds1337.h +@@ -0,0 +1,43 @@ ++/* ++ * ds1337.h ++ * ++ * Copyright (C) 2003 M&N Logistik-Lösungen Online 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. ++ * ++ */ ++#ifndef DS1337_H ++#define DS1337_H ++ ++#define DS1337_I2C_SLAVE_ADDR 0x68 ++//#define DS1337_RAM_ADDR_START 0x10 ++//#define DS1337_RAM_ADDR_END 0x10 ++#define DS1337_MEM_SIZE 0x10 ++ ++#define DS1337_PROC_NAME "driver/ds1337" ++ ++struct rtc_mem { ++ unsigned int loc; ++ unsigned int nr; ++ unsigned char *data; ++}; ++ ++#define DS1337_GETDATETIME 0 ++#define DS1337_SETTIME 1 ++#define DS1337_SETDATETIME 2 ++ ++#define DS1337_RATE_1HZ 0x00 /* Rate Select 1 Hz */ ++#define DS1337_RATE_4096HZ 0x01 /* Rate Select 4096 kHz */ ++#define DS1337_RATE_8192HZ 0x02 /* Rate Select 8192 kHz */ ++#define DS1337_RATE_32768HZ 0x03 /* Rate Select 32768 kHz */ ++ ++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) ++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) ++ ++#define DS1337_HOUR12 0x40 ++#define DS1337_HOUR24 0x00 ++#define DS1337_HOURS_24(val) BCD_TO_BIN((val & 0x3f)) ++ ++#endif +--- /dev/null ++++ linux-2.4.21/drivers/char/german.map +@@ -0,0 +1,528 @@ ++keymaps 0-2,4-6,8-10,12 ++keycode 1 = Escape Escape ++ alt keycode 1 = Meta_Escape ++ shift alt keycode 1 = Meta_Escape ++keycode 2 = one exclam ++ alt keycode 2 = Meta_one ++ shift alt keycode 2 = Meta_exclam ++keycode 3 = two quotedbl twosuperior nul ++ alt keycode 3 = Meta_two ++ shift alt keycode 3 = Meta_quotedbl ++ control alt keycode 3 = Meta_nul ++keycode 4 = three section threesuperior Escape ++ alt keycode 4 = Meta_three ++ control alt keycode 4 = Meta_Escape ++keycode 5 = four dollar ++ alt keycode 5 = Meta_four ++ shift alt keycode 5 = Meta_dollar ++keycode 6 = five percent ++ alt keycode 6 = Meta_five ++ shift alt keycode 6 = Meta_percent ++keycode 7 = six ampersand ++ control keycode 7 = Control_asciicircum ++ alt keycode 7 = Meta_six ++ shift alt keycode 7 = Meta_ampersand ++keycode 8 = seven slash braceleft ++ alt keycode 8 = Meta_seven ++ shift alt keycode 8 = Meta_slash ++ altgr alt keycode 8 = Meta_braceleft ++keycode 9 = eight parenleft bracketleft ++ alt keycode 9 = Meta_eight ++ shift alt keycode 9 = Meta_parenleft ++ altgr alt keycode 9 = Meta_bracketleft ++keycode 10 = nine parenright bracketright ++ altgr control keycode 10 = Control_bracketright ++ alt keycode 10 = Meta_nine ++ shift alt keycode 10 = Meta_parenright ++ altgr alt keycode 10 = Meta_bracketright ++keycode 11 = zero equal braceright ++ alt keycode 11 = Meta_zero ++ shift alt keycode 11 = Meta_equal ++ altgr alt keycode 11 = Meta_braceright ++keycode 12 = ssharp question backslash ++ altgr control keycode 12 = Control_backslash ++ shift alt keycode 12 = Meta_question ++ altgr alt keycode 12 = Meta_backslash ++keycode 13 = apostrophe grave ++ alt keycode 13 = 0x08b4 ++ shift alt keycode 13 = Meta_grave ++keycode 14 = BackSpace Delete ++ alt keycode 14 = Meta_BackSpace ++ shift alt keycode 14 = Meta_Delete ++keycode 15 = Tab Tab ++ alt keycode 15 = Meta_Tab ++ shift alt keycode 15 = Meta_Tab ++keycode 16 = +q +Q at Control_q Control_q Control_q Meta_q Meta_Q Meta_at Meta_Control_q ++keycode 17 = w ++keycode 18 = +e +E currency Control_e Control_e Control_e Meta_e Meta_E Meta_e Meta_Control_e ++keycode 19 = r ++keycode 20 = t ++keycode 21 = z ++keycode 22 = u ++keycode 23 = i ++keycode 24 = o ++keycode 25 = p ++keycode 26 = +udiaeresis +Udiaeresis ++keycode 27 = plus asterisk asciitilde ++ alt keycode 27 = Meta_plus ++ shift alt keycode 27 = Meta_asterisk ++keycode 28 = Return ++ alt keycode 28 = Meta_Control_m ++keycode 29 = Control ++keycode 30 = a ++keycode 31 = s ++keycode 32 = d ++keycode 33 = f ++keycode 34 = g ++keycode 35 = h ++keycode 36 = j ++keycode 37 = k ++keycode 38 = l ++keycode 39 = +odiaeresis +Odiaeresis ++keycode 40 = +adiaeresis +Adiaeresis ++keycode 41 = asciicircum degree Meta_asciicircum Control_asciicircum ++ control alt keycode 41 = Meta_Control_asciicircum ++keycode 42 = Shift ++keycode 43 = numbersign apostrophe ++ alt keycode 43 = Meta_numbersign ++ shift alt keycode 43 = Meta_apostrophe ++keycode 44 = y ++keycode 45 = x ++keycode 46 = c ++keycode 47 = v ++keycode 48 = b ++keycode 49 = n ++keycode 50 = +m +M mu Control_m Control_m Control_m Meta_m Meta_M Meta_m Meta_Control_m ++keycode 51 = comma semicolon ++ alt keycode 51 = Meta_comma ++ shift alt keycode 51 = Meta_semicolon ++keycode 52 = period colon ++ alt keycode 52 = Meta_period ++ shift alt keycode 52 = Meta_colon ++keycode 53 = minus underscore Meta_minus ++ shift control keycode 53 = Control_underscore ++ alt keycode 53 = Meta_minus ++ shift alt keycode 53 = Meta_underscore ++keycode 54 = Shift ++keycode 55 = KP_Multiply ++ altgr keycode 55 = Hex_C ++keycode 56 = Alt ++keycode 57 = space space Meta_space nul ++ alt keycode 57 = Meta_space ++ shift alt keycode 57 = Meta_space ++ control alt keycode 57 = Meta_nul ++keycode 58 = Caps_Lock ++keycode 59 = F1 F13 Console_13 F25 ++ altgr control keycode 59 = F1 ++ alt keycode 59 = Console_1 ++ control alt keycode 59 = Console_1 ++keycode 60 = F2 F14 Console_14 F26 ++ altgr control keycode 60 = F2 ++ alt keycode 60 = Console_2 ++ control alt keycode 60 = Console_2 ++keycode 61 = F3 F15 Console_15 F27 ++ altgr control keycode 61 = F3 ++ alt keycode 61 = Console_3 ++ control alt keycode 61 = Console_3 ++keycode 62 = F4 F16 Console_16 F28 ++ altgr control keycode 62 = F4 ++ alt keycode 62 = Console_4 ++ control alt keycode 62 = Console_4 ++keycode 63 = F5 F17 Console_17 F29 ++ altgr control keycode 63 = F5 ++ alt keycode 63 = Console_5 ++ control alt keycode 63 = Console_5 ++keycode 64 = F6 F18 Console_18 F30 ++ altgr control keycode 64 = F6 ++ alt keycode 64 = Console_6 ++ control alt keycode 64 = Console_6 ++keycode 65 = F7 F19 Console_19 F31 ++ altgr control keycode 65 = F7 ++ alt keycode 65 = Console_7 ++ control alt keycode 65 = Console_7 ++keycode 66 = F8 F20 Console_20 F32 ++ altgr control keycode 66 = F8 ++ alt keycode 66 = Console_8 ++ control alt keycode 66 = Console_8 ++keycode 67 = F9 F21 Console_21 F33 ++ altgr control keycode 67 = F9 ++ alt keycode 67 = Console_9 ++ control alt keycode 67 = Console_9 ++keycode 68 = F10 F22 Console_22 F34 ++ altgr control keycode 68 = F10 ++ alt keycode 68 = Console_10 ++ control alt keycode 68 = Console_10 ++keycode 69 = Num_Lock ++ altgr keycode 69 = Hex_A ++keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State ++ alt keycode 70 = Scroll_Lock ++keycode 71 = KP_7 ++ altgr keycode 71 = Hex_7 ++ alt keycode 71 = Ascii_7 ++keycode 72 = KP_8 ++ altgr keycode 72 = Hex_8 ++ alt keycode 72 = Ascii_8 ++keycode 73 = KP_9 ++ altgr keycode 73 = Hex_9 ++ alt keycode 73 = Ascii_9 ++keycode 74 = KP_Subtract ++ altgr keycode 74 = Hex_D ++keycode 75 = KP_4 ++ altgr keycode 75 = Hex_4 ++ alt keycode 75 = Ascii_4 ++keycode 76 = KP_5 ++ altgr keycode 76 = Hex_5 ++ alt keycode 76 = Ascii_5 ++keycode 77 = KP_6 ++ altgr keycode 77 = Hex_6 ++ alt keycode 77 = Ascii_6 ++keycode 78 = KP_Add ++ altgr keycode 78 = Hex_E ++keycode 79 = KP_1 ++ altgr keycode 79 = Hex_1 ++ alt keycode 79 = Ascii_1 ++keycode 80 = KP_2 ++ altgr keycode 80 = Hex_2 ++ alt keycode 80 = Ascii_2 ++keycode 81 = KP_3 ++ altgr keycode 81 = Hex_3 ++ alt keycode 81 = Ascii_3 ++keycode 82 = KP_0 ++ altgr keycode 82 = Hex_0 ++ alt keycode 82 = Ascii_0 ++keycode 83 = KP_Comma ++ altgr control keycode 83 = Boot ++ control alt keycode 83 = Boot ++#keycode 84 = Last_Console ++keycode 85 = ++keycode 86 = less greater bar ++ alt keycode 86 = Meta_less ++ shift alt keycode 86 = Meta_greater ++ altgr alt keycode 86 = Meta_bar ++keycode 87 = F11 F23 Console_23 F35 ++ altgr control keycode 87 = F11 ++ alt keycode 87 = Console_11 ++ control alt keycode 87 = Console_11 ++keycode 88 = F12 F24 Console_24 F36 ++ altgr control keycode 88 = F12 ++ alt keycode 88 = Console_12 ++ control alt keycode 88 = Console_12 ++keycode 89 = slash question degree ++ alt keycode 89 = Meta_slash ++ shift alt keycode 89 = Meta_question ++keycode 90 = ++keycode 91 = ++keycode 92 = ++keycode 93 = ++keycode 94 = ++keycode 95 = ++keycode 96 = KP_Enter ++ altgr keycode 96 = Hex_F ++keycode 97 = Control ++keycode 98 = KP_Divide ++ altgr keycode 98 = Hex_B ++keycode 99 = Compose ++keycode 100 = AltGr ++ alt keycode 100 = Compose ++keycode 101 = Break ++keycode 102 = Find ++keycode 103 = Up ++ alt keycode 103 = KeyboardSignal ++keycode 104 = Prior ++ shift keycode 104 = Scroll_Backward ++keycode 105 = Left ++# alt keycode 105 = Decr_Console ++keycode 106 = Right ++# alt keycode 106 = Incr_Console ++keycode 107 = Select ++keycode 108 = Down ++keycode 109 = Next ++ shift keycode 109 = Scroll_Forward ++keycode 110 = Insert ++keycode 111 = Remove ++ altgr control keycode 111 = Boot ++ control alt keycode 111 = Boot ++keycode 112 = Macro ++ shift alt keycode 112 = VoidSymbol ++ altgr alt keycode 112 = VoidSymbol ++keycode 113 = F13 ++ shift alt keycode 113 = VoidSymbol ++ altgr alt keycode 113 = VoidSymbol ++keycode 114 = F14 ++ shift alt keycode 114 = VoidSymbol ++ altgr alt keycode 114 = VoidSymbol ++keycode 115 = Help ++ shift alt keycode 115 = VoidSymbol ++ altgr alt keycode 115 = VoidSymbol ++keycode 116 = Do ++ shift alt keycode 116 = VoidSymbol ++ altgr alt keycode 116 = VoidSymbol ++keycode 117 = F17 ++ shift alt keycode 117 = VoidSymbol ++ altgr alt keycode 117 = VoidSymbol ++keycode 118 = KP_MinPlus ++ shift alt keycode 118 = VoidSymbol ++ altgr alt keycode 118 = VoidSymbol ++keycode 119 = Pause ++keycode 120 = ++keycode 121 = ++keycode 122 = ++keycode 123 = ++keycode 124 = ++#keycode 125 = Decr_Console ++#keycode 126 = Incr_Console ++keycode 127 = Compose ++string F1 = "\033[[A" ++string F2 = "\033[[B" ++string F3 = "\033[[C" ++string F4 = "\033[[D" ++string F5 = "\033[[E" ++string F6 = "\033[17~" ++string F7 = "\033[18~" ++string F8 = "\033[19~" ++string F9 = "\033[20~" ++string F10 = "\033[21~" ++string F11 = "\033[23~" ++string F12 = "\033[24~" ++string F13 = "\033[25~" ++string F14 = "\033[26~" ++string F15 = "\033[28~" ++string F16 = "\033[29~" ++string F17 = "\033[31~" ++string F18 = "\033[32~" ++string F19 = "\033[33~" ++string F20 = "\033[34~" ++string Find = "\033[1~" ++string Insert = "\033[2~" ++string Remove = "\033[3~" ++string Select = "\033[4~" ++string Prior = "\033[5~" ++string Next = "\033[6~" ++string Macro = "\033[M" ++string Pause = "\033[P" ++compose '!' '!' to '¡' ++compose '"' 'A' to 'Ä' ++compose '"' 'E' to 'Ë' ++compose '"' 'I' to 'Ï' ++compose '"' 'O' to 'Ö' ++compose '"' 'U' to 'Ü' ++compose '"' 'Y' to '¾' ++compose '"' 'a' to 'ä' ++compose '"' 'c' to '©' ++compose '"' 'e' to 'ë' ++compose '"' 'i' to 'ï' ++compose '"' 'o' to 'ö' ++compose '"' 'r' to '®' ++compose '"' 'u' to 'ü' ++compose '"' 'y' to 'ÿ' ++compose '(' 'c' to '©' ++compose '(' 'r' to '®' ++compose '+' '-' to '±' ++compose ',' 'A' to '¡' ++compose ',' 'C' to 'Ç' ++compose ',' 'E' to 'Ê' ++compose ',' 'G' to '«' ++compose ',' 'I' to 'Ç' ++compose ',' 'K' to 'Ó' ++compose ',' 'L' to '¦' ++compose ',' 'N' to 'Ñ' ++compose ',' 'R' to '£' ++compose ',' 'S' to 'ª' ++compose ',' 'T' to 'Þ' ++compose ',' 'U' to 'Ù' ++compose ',' 'a' to '±' ++compose ',' 'c' to 'ç' ++compose ',' 'e' to 'ê' ++compose ',' 'g' to '»' ++compose ',' 'i' to 'ç' ++compose ',' 'k' to 'ó' ++compose ',' 'l' to '¶' ++compose ',' 'n' to 'ñ' ++compose ',' 'r' to '³' ++compose ',' 's' to 'º' ++compose ',' 't' to 'þ' ++compose ',' 'u' to 'ù' ++compose '-' ':' to '÷' ++compose '-' 'A' to 'ª' ++compose '-' 'C' to '¢' ++compose '-' 'D' to 'Ð' ++compose '-' 'E' to '¤' ++compose '-' 'H' to '¡' ++compose '-' 'L' to '£' ++compose '-' 'O' to 'º' ++compose '-' 'T' to '¬' ++compose '-' 'Y' to '¥' ++compose '-' 'a' to 'ª' ++compose '-' 'c' to '¢' ++compose '-' 'd' to 'ð' ++compose '-' 'e' to '¤' ++compose '-' 'h' to '±' ++compose '-' 'l' to '£' ++compose '-' 'l' to '¥' ++compose '-' 'l' to '³' ++compose '-' 'o' to 'º' ++compose '-' 't' to '¼' ++compose '.' '.' to '·' ++compose '.' 'C' to 'Å' ++compose '.' 'C' to 'Õ' ++compose '.' 'E' to 'Ì' ++compose '.' 'I' to '©' ++compose '.' 'Z' to '¯' ++compose '.' 'c' to 'å' ++compose '.' 'c' to 'õ' ++compose '.' 'e' to 'ì' ++compose '.' 'i' to '¹' ++compose '.' 'z' to '¿' ++compose '/' 'D' to 'Ð' ++compose '/' 'L' to '£' ++compose '/' 'O' to 'Ø' ++compose '/' 'T' to '¬' ++compose '/' 'c' to '¢' ++compose '/' 'd' to 'ð' ++compose '/' 'l' to '³' ++compose '/' 'o' to 'ø' ++compose '/' 't' to '¼' ++compose '0' 'A' to 'Å' ++compose '0' 'U' to 'Ù' ++compose '0' 'a' to 'å' ++compose '0' 'u' to 'ù' ++compose '1' '2' to '½' ++compose '1' '4' to '¼' ++compose '3' '4' to '¾' ++compose ':' '-' to '÷' ++compose ':' 'A' to 'Ä' ++compose ':' 'E' to 'Ë' ++compose ':' 'O' to 'Ö' ++compose ':' 'U' to 'Ü' ++compose ':' 'a' to 'ä' ++compose ':' 'e' to 'ë' ++compose ':' 'o' to 'ö' ++compose ':' 'u' to 'ü' ++compose '<' '<' to '«' ++compose '>' '>' to '»' ++compose '?' '?' to '¿' ++compose 'A' 'A' to 'Å' ++compose 'A' 'E' to 'Æ' ++compose 'I' 'J' to '¾' ++compose 'L' '=' to '£' ++compose 'N' 'G' to '½' ++compose 'N' 'H' to 'Ñ' ++compose 'N' 'N' to 'Ñ' ++compose 'N' 'Y' to 'Ñ' ++compose 'N' 'h' to 'Ñ' ++compose 'N' 'n' to 'Ñ' ++compose 'N' 'y' to 'Ñ' ++compose 'O' 'A' to 'Å' ++compose 'O' 'E' to '¼' ++compose 'O' 'e' to '¼' ++compose 'T' 'H' to 'Þ' ++compose 'U' 'U' to 'Ù' ++compose 'Y' '=' to '¥' ++compose '\'' 'A' to 'Á' ++compose '\'' 'C' to 'Æ' ++compose '\'' 'E' to 'É' ++compose '\'' 'I' to 'Í' ++compose '\'' 'L' to 'Å' ++compose '\'' 'N' to 'Ñ' ++compose '\'' 'O' to 'Ó' ++compose '\'' 'R' to 'À' ++compose '\'' 'S' to '¦' ++compose '\'' 'U' to 'Ú' ++compose '\'' 'Y' to 'Ý' ++compose '\'' 'Z' to '¬' ++compose '\'' 'a' to 'á' ++compose '\'' 'c' to 'æ' ++compose '\'' 'e' to 'é' ++compose '\'' 'i' to 'í' ++compose '\'' 'l' to 'å' ++compose '\'' 'n' to 'ñ' ++compose '\'' 'o' to 'ó' ++compose '\'' 'r' to 'à' ++compose '\'' 's' to '¶' ++compose '\'' 'u' to 'ú' ++compose '\'' 'y' to 'ý' ++compose '\'' 'z' to '¼' ++compose '^' '!' to '¡' ++compose '^' '*' to '×' ++compose '^' '.' to '·' ++compose '^' '/' to '÷' ++compose '^' '1' to '¹' ++compose '^' '2' to '²' ++compose '^' '3' to '³' ++compose '^' ':' to '÷' ++compose '^' '?' to '¿' ++compose '^' 'A' to 'Â' ++compose '^' 'C' to 'Ç' ++compose '^' 'D' to 'Ð' ++compose '^' 'E' to 'Ê' ++compose '^' 'G' to 'Ô' ++compose '^' 'H' to '¦' ++compose '^' 'I' to 'Î' ++compose '^' 'J' to '¬' ++compose '^' 'L' to '¥' ++compose '^' 'N' to 'Ñ' ++compose '^' 'R' to 'Ø' ++compose '^' 'S' to '¦' ++compose '^' 'T' to '«' ++compose '^' 'U' to 'Û' ++compose '^' 'Z' to '´' ++compose '^' 'a' to 'â' ++compose '^' 'c' to 'ç' ++compose '^' 'd' to 'ð' ++compose '^' 'e' to 'ê' ++compose '^' 'g' to 'ø' ++compose '^' 'h' to '¶' ++compose '^' 'i' to 'î' ++compose '^' 'j' to '¼' ++compose '^' 'l' to 'µ' ++compose '^' 'n' to 'ñ' ++compose '^' 'o' to 'ô' ++compose '^' 'r' to 'ø' ++compose '^' 's' to '¨' ++compose '^' 't' to '»' ++compose '^' 'u' to 'û' ++compose '^' 'x' to '×' ++compose '^' 'z' to '¸' ++compose '`' 'A' to 'À' ++compose '`' 'E' to 'È' ++compose '`' 'I' to 'Ì' ++compose '`' 'O' to 'Ò' ++compose '`' 'U' to 'Ù' ++compose '`' 'a' to 'à' ++compose '`' 'e' to 'è' ++compose '`' 'i' to 'ì' ++compose '`' 'o' to 'ò' ++compose '`' 'u' to 'ù' ++compose 'a' 'a' to 'å' ++compose 'a' 'e' to 'æ' ++compose 'c' '/' to '¢' ++compose 'c' '=' to '¢' ++compose 'e' '=' to '¤' ++compose 'i' 'j' to 'ÿ' ++compose 'm' 'u' to 'µ' ++compose 'n' 'g' to '¿' ++compose 'n' 'h' to 'ñ' ++compose 'n' 'n' to 'ñ' ++compose 'o' 'a' to 'å' ++compose 'o' 'e' to '½' ++compose 's' 's' to 'ß' ++compose 's' 'z' to 'ß' ++compose 't' 'h' to 'þ' ++compose 'u' 'u' to 'ù' ++compose 'v' 'S' to '¦' ++compose 'v' 'Z' to '´' ++compose 'v' 's' to '¨' ++compose 'v' 'z' to '¸' ++compose 'x' 'x' to '×' ++compose '~' 'A' to 'Ã' ++compose '~' 'G' to '«' ++compose '~' 'I' to '¥' ++compose '~' 'N' to 'Ñ' ++compose '~' 'O' to 'Õ' ++compose '~' 'U' to 'Ý' ++compose '~' 'a' to 'ã' ++compose '~' 'g' to '»' ++compose '~' 'i' to 'µ' ++compose '~' 'n' to 'ñ' ++compose '~' 'o' to 'õ' ++compose '~' 'u' to 'ý' +--- /dev/null ++++ linux-2.4.21/drivers/char/input_keyb.c +@@ -0,0 +1,167 @@ ++/* ++ * linux/drivers/char/input_keyb.c by Russ Dill ++ * taken from pc_keyb.c ++ * ++ * This code grabs keypresses from the input layer and makes them ++ * available to the console. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++ ++#include ++#include ++ ++#include ++#include ++ ++/* Simple translation table for the SysRq keys */ ++ ++unsigned char input_sysrq_xlate[128] = ++ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++ ++/* ++ * Translation of escaped scancodes to keycodes. ++ * This is now user-settable. ++ * The keycodes 1-88,96-111,119 are fairly standard, and ++ * should probably not be changed - changing might confuse X. ++ * X also interprets scancode 0x5d (KEY_Begin). ++ * ++ * For 1-88 keycode equals scancode. ++ */ ++ ++#define E0_KPENTER 96 ++#define E0_RCTRL 97 ++#define E0_KPSLASH 98 ++#define E0_PRSCR 99 ++#define E0_RALT 100 ++#define E0_BREAK 101 /* (control-pause) */ ++#define E0_HOME 102 ++#define E0_UP 103 ++#define E0_PGUP 104 ++#define E0_LEFT 105 ++#define E0_RIGHT 106 ++#define E0_END 107 ++#define E0_DOWN 108 ++#define E0_PGDN 109 ++#define E0_INS 110 ++#define E0_DEL 111 ++ ++#define E1_PAUSE 119 ++ ++/* ++ * New microsoft keyboard is rumoured to have ++ * e0 5b (left window button), e0 5c (right window button), ++ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] ++ * [or: Windows_L, Windows_R, TaskMan] ++ */ ++#define E0_MSLW 125 ++ ++static unsigned char e0_keys[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ ++ 0, 0, 0, 0, 0, E0_RCTRL, 0, 0, /* 0x18-0x1f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ ++ 0, 0, 0, 0, 0, 0, 0, E0_PRSCR, /* 0x30-0x37 */ ++ E0_RALT, 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f */ ++ 0, 0, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ ++ E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, 0, E0_END, /* 0x48-0x4f */ ++ E0_DOWN, E0_PGDN, 0, 0, 0, 0, 0, 0, /* 0x50-0x57 */ ++ 0, 0, 0, E0_MSLW, 0, 0, 0, 0, /* 0x58-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ ++ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ ++}; ++ ++int input_setkeycode(unsigned int scancode, unsigned int keycode) ++{ ++ if (scancode > 255 || keycode > 127) return -EINVAL; ++ e0_keys[scancode - 128] = keycode; ++ return 0; ++} ++ ++int input_getkeycode(unsigned int scancode) ++{ ++ return scancode > 255 ? -EINVAL : e0_keys[scancode - 128]; ++} ++ ++#define KBD_REPORT_UNKN ++int input_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ static int prev_scancode; ++ ++ /* special prefix scancodes.. */ ++ if (scancode == 0xe0 || scancode == 0xe1) { ++ prev_scancode = scancode; ++ return 0; ++ } ++ if (prev_scancode) { ++ /* ++ * usually it will be 0xe0, but a Pause key generates ++ * e1 1d 45 e1 9d c5 when pressed, and nothing when released ++ */ ++ if (prev_scancode != 0xe0) { ++ if (prev_scancode == 0xe1 && scancode == 0x1d) { ++ prev_scancode = 0x100; ++ return 0; ++ } else if (prev_scancode == 0x100 && scancode == 0x45) { ++ *keycode = E1_PAUSE; ++ prev_scancode = 0; ++ } else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); ++#endif ++ prev_scancode = 0; ++ return 0; ++ } ++ } else { ++ prev_scancode = 0; ++ ++ if (e0_keys[scancode]) ++ *keycode = e0_keys[scancode]; ++ else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", ++ scancode); ++#endif ++ return 0; ++ } ++ } ++ } else ++ *keycode = scancode; ++ return 1; ++} ++ ++char input_unexpected_up(unsigned char keycode) ++{ ++ return 0200; ++} ++ ++/* Allow for loadable keyboard drivers */ ++EXPORT_SYMBOL(input_setkeycode); ++EXPORT_SYMBOL(input_unexpected_up); ++EXPORT_SYMBOL(input_translate); ++EXPORT_SYMBOL(input_sysrq_xlate); ++EXPORT_SYMBOL(input_getkeycode); ++EXPORT_SYMBOL(k_setkeycode); ++EXPORT_SYMBOL(k_unexpected_up); ++EXPORT_SYMBOL(k_translate); ++EXPORT_SYMBOL(k_getkeycode); ++#ifdef CONFIG_MAGIC_SYSRQ ++EXPORT_SYMBOL(k_sysrq_key); ++EXPORT_SYMBOL(k_sysrq_xlate); ++#endif +--- linux-2.4.21/drivers/char/keyboard.c~wedge ++++ linux-2.4.21/drivers/char/keyboard.c +@@ -77,6 +77,7 @@ + void (*kbd_ledfunc)(unsigned int led); + EXPORT_SYMBOL(handle_scancode); + EXPORT_SYMBOL(kbd_ledfunc); ++EXPORT_SYMBOL(key_maps); + EXPORT_SYMBOL(kbd_refresh_leds); + + extern void ctrl_alt_del(void); +--- linux-2.4.21/drivers/char/serial.c~ramses-serial ++++ linux-2.4.21/drivers/char/serial.c +@@ -1,138 +1,8 @@ +-/* +- * linux/drivers/char/serial.c +- * +- * Copyright (C) 1991, 1992 Linus Torvalds +- * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, +- * 1998, 1999 Theodore Ts'o +- * +- * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now +- * much more extensible to support other serial cards based on the +- * 16450/16550A UART's. Added support for the AST FourPort and the +- * Accent Async board. +- * +- * set_serial_info fixed to set the flags, custom divisor, and uart +- * type fields. Fix suggested by Michael K. Johnson 12/12/92. +- * +- * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis +- * +- * 03/96: Modularised by Angelo Haritsis +- * +- * rs_set_termios fixed to look also for changes of the input +- * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. +- * Bernd Anhäupl 05/17/96. +- * +- * 1/97: Extended dumb serial ports are a config option now. +- * Saves 4k. Michael A. Griffith +- * +- * 8/97: Fix bug in rs_set_termios with RTS +- * Stanislav V. Voronyi +- * +- * 3/98: Change the IRQ detection, use of probe_irq_o*(), +- * suppress TIOCSERGWILD and TIOCSERSWILD +- * Etienne Lorrain +- * +- * 4/98: Added changes to support the ARM architecture proposed by +- * Russell King +- * +- * 5/99: Updated to include support for the XR16C850 and ST16C654 +- * uarts. Stuart MacDonald +- * +- * 8/99: Generalized PCI support added. Theodore Ts'o +- * +- * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a +- * few races on freeing buffers too. +- * Alan Modra +- * +- * 5/00: Support for the RSA-DV II/S card added. +- * Kiyokazu SUTO +- * +- * 6/00: Remove old-style timer, use timer_list +- * Andrew Morton +- * +- * 7/00: Support Timedia/Sunix/Exsys PCI cards +- * +- * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. +- * Arnaldo Carvalho de Melo +- * +- * 10/00: add in optional software flow control for serial console. +- * Kanoj Sarcar (Modified by Theodore Ts'o) +- * +- * 02/02: Fix for AMD Elan bug in transmit irq routine, by +- * Christer Weinigel , +- * Robert Schwebel , +- * Juergen Beisert , +- * Theodore Ts'o +- */ +- +-static char *serial_version = "5.05c"; +-static char *serial_revdate = "2001-07-08"; +- +-/* +- * Serial driver configuration section. Here are the various options: +- * +- * CONFIG_HUB6 +- * Enables support for the venerable Bell Technologies +- * HUB6 card. +- * +- * CONFIG_SERIAL_MANY_PORTS +- * Enables support for ports beyond the standard, stupid +- * COM 1/2/3/4. +- * +- * CONFIG_SERIAL_MULTIPORT +- * Enables support for special multiport board support. +- * +- * CONFIG_SERIAL_SHARE_IRQ +- * Enables support for multiple serial ports on one IRQ +- * +- * CONFIG_SERIAL_DETECT_IRQ +- * Enable the autodetection of IRQ on standart ports +- * +- * SERIAL_PARANOIA_CHECK +- * Check the magic number for the async_structure where +- * ever possible. +- * +- * CONFIG_SERIAL_ACPI +- * Enable support for serial console port and serial +- * debug port as defined by the SPCR and DBGP tables in +- * ACPI 2.0. +- */ ++#undef DEBUG + + #include + #include + +-#undef SERIAL_PARANOIA_CHECK +-#define CONFIG_SERIAL_NOPAUSE_IO +-#define SERIAL_DO_RESTART +- +-#if 0 +-/* These defines are normally controlled by the autoconf.h */ +-#define CONFIG_SERIAL_MANY_PORTS +-#define CONFIG_SERIAL_SHARE_IRQ +-#define CONFIG_SERIAL_DETECT_IRQ +-#define CONFIG_SERIAL_MULTIPORT +-#define CONFIG_HUB6 +-#endif +- +-#ifdef CONFIG_PCI +-#define ENABLE_SERIAL_PCI +-#ifndef CONFIG_SERIAL_SHARE_IRQ +-#define CONFIG_SERIAL_SHARE_IRQ +-#endif +-#ifndef CONFIG_SERIAL_MANY_PORTS +-#define CONFIG_SERIAL_MANY_PORTS +-#endif +-#endif +- +-#ifdef CONFIG_SERIAL_ACPI +-#define ENABLE_SERIAL_ACPI +-#endif +- +-#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +-#ifndef ENABLE_SERIAL_PNP +-#define ENABLE_SERIAL_PNP +-#endif +-#endif +- + #ifdef CONFIG_ARCH_PXA + #define pxa_port(x) ((x) == PORT_PXA) + #define pxa_buggy_port(x) ({ \ +@@ -149,39 +19,16 @@ + #undef SERIAL_DEBUG_OPEN + #undef SERIAL_DEBUG_FLOW + #undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +-#undef SERIAL_DEBUG_PCI +-#undef SERIAL_DEBUG_AUTOCONF + + /* Sanity checks */ + +-#ifdef CONFIG_SERIAL_MULTIPORT +-#ifndef CONFIG_SERIAL_SHARE_IRQ +-#define CONFIG_SERIAL_SHARE_IRQ +-#endif +-#endif +- +-#ifdef CONFIG_HUB6 +-#ifndef CONFIG_SERIAL_MANY_PORTS +-#define CONFIG_SERIAL_MANY_PORTS +-#endif +-#ifndef CONFIG_SERIAL_SHARE_IRQ +-#define CONFIG_SERIAL_SHARE_IRQ +-#endif +-#endif +- + #ifdef MODULE + #undef CONFIG_SERIAL_CONSOLE + #endif + +-#define CONFIG_SERIAL_RSA +- + #define RS_STROBE_TIME (10*HZ) + #define RS_ISR_PASS_LIMIT 256 + +-#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +-#define SERIAL_INLINE +-#endif +- + /* + * End of serial driver configuration section. + */ +@@ -213,53 +60,51 @@ + #include + #include + #include +-#if (LINUX_VERSION_CODE >= 131343) + #include +-#endif +-#if (LINUX_VERSION_CODE >= 131336) + #include +-#endif + #include + #ifdef CONFIG_SERIAL_CONSOLE + #include + #endif +-#ifdef ENABLE_SERIAL_PCI +-#include +-#endif +-#ifdef ENABLE_SERIAL_PNP +-#include +-#endif + #ifdef CONFIG_MAGIC_SYSRQ + #include + #endif + +-/* +- * All of the compatibilty code so we can compile serial.c against +- * older kernels is hidden in serial_compat.h +- */ +-#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +-#include "serial_compat.h" +-#endif +- + #include + #include + #include + #include + +-#if defined(CONFIG_MAC_SERIAL) +-#define SERIAL_DEV_OFFSET ((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2) +-#else +-#define SERIAL_DEV_OFFSET 0 +-#endif ++#define _INLINE_ + +-#ifdef SERIAL_INLINE +-#define _INLINE_ inline ++/* ++ * The TI16754 has 4 UARTS. They are selected with nCS3 and some ++ * address bits: ++ * ++ * 12 8 4 ++ * nA8, nCS[3] for MN_UART_1, address mask 1110 1110 0000 0000 = 0xEE00 ++ * nA9, nCS[3] for MN_UART_1, address mask 1110 1101 0000 0000 = 0xED00 ++ * nA10, nCS[3] for MN_UART_1, address mask 1110 1011 0000 0000 = 0xEB00 ++ * nA11, nCS[3] for MN_UART_1, address mask 1110 0111 0000 0000 = 0xE700 ++ */ ++#define RAMSES_UARTA_PHYS (PXA_CS3_PHYS+0xEE00) ++#define RAMSES_UARTB_PHYS (PXA_CS3_PHYS+0xED00) ++#define RAMSES_UARTC_PHYS (PXA_CS3_PHYS+0xEB00) ++#define RAMSES_UARTD_PHYS (PXA_CS3_PHYS+0xE700) ++static void *ramses_uarta; // address cookie for UART A ++static void *ramses_uartb; // address cookie for UART B ++static void *ramses_uartc; // address cookie for UART C/Scanner ++static void *ramses_uartd; // address cookie for UART D ++static int ramses_stay_on = 0; ++ ++#ifdef DEBUG ++#define DPRINTK(fmt,args...) printk("//HS " fmt, ## args) ++static int show_io = 1; + #else +-#define _INLINE_ ++#define DPRINTK(fmt,args...) ++static int show_io = 0; + #endif + +-static char *serial_name = "Serial driver"; +- + static DECLARE_TASK_QUEUE(tq_serial); + + static struct tty_driver serial_driver, callout_driver; +@@ -282,9 +127,6 @@ + */ + + static struct async_struct *IRQ_ports[NR_IRQS]; +-#ifdef CONFIG_SERIAL_MULTIPORT +-static struct rs_multiport_struct rs_multiport[NR_IRQS]; +-#endif + static int IRQ_timeout[NR_IRQS]; + #ifdef CONFIG_SERIAL_CONSOLE + static struct console sercons; +@@ -294,8 +136,6 @@ + static unsigned long break_pressed; /* break, really ... */ + #endif + +-static unsigned detect_uart_irq (struct serial_state * state); +-static void autoconfig(struct serial_state * state); + static void change_speed(struct async_struct *info, struct termios *old); + static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +@@ -325,46 +165,74 @@ + { 0, 0} + }; + +-#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) +- +-#define PORT_RSA_MAX 4 +-static int probe_rsa[PORT_RSA_MAX]; +-static int force_rsa[PORT_RSA_MAX]; +- +-MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +-MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +-MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +-MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +-#endif /* CONFIG_SERIAL_RSA */ +- +-struct serial_state rs_table[RS_TABLE_SIZE] = { +- SERIAL_PORT_DFNS /* Defined in serial.h */ ++static struct serial_state rs_table[] = { ++ { ++ type: PORT_PXA, ++ xmit_fifo_size: 32, ++ baud_base: 921600, ++ iomem_base: (void *)&FFUART, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM32, ++ irq: IRQ_FFUART, ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_PXA, ++ xmit_fifo_size: 32, ++ baud_base: 921600, ++ iomem_base: (void *)&BTUART, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM32, ++ irq: IRQ_BTUART, ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_PXA, ++ xmit_fifo_size: 32, ++ baud_base: 921600, ++ iomem_base: (void *)&STUART, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM32, ++ irq: IRQ_STUART, ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_16750, ++ xmit_fifo_size: 64, ++ baud_base: 115200*2, ++ iomem_base: (void *)0, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM, ++ irq: IRQ_GPIO(7), ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_16750, ++ xmit_fifo_size: 64, ++ baud_base: 115200*2, ++ iomem_base: (void *)0, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM, ++ irq: IRQ_GPIO(24), ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_16750, ++ xmit_fifo_size: 64, ++ baud_base: 115200*2, ++ iomem_base: (void *)0, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM, ++ irq: IRQ_GPIO(25), ++ flags: ASYNC_SKIP_TEST, ++ }, { ++ type: PORT_16750, ++ xmit_fifo_size: 64, ++ baud_base: 115200*2, ++ iomem_base: (void *)0, ++ iomem_reg_shift: 2, ++ io_type: SERIAL_IO_MEM, ++ irq: IRQ_GPIO(26), ++ flags: ASYNC_SKIP_TEST, ++ } + }; + + #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) +-int serial_nr_ports = NR_PORTS; +- +-#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +-#define NR_PCI_BOARDS 8 +- +-static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; +- +-#ifndef IS_PCI_REGION_IOPORT +-#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ +- IORESOURCE_IO) +-#endif +-#ifndef IS_PCI_REGION_IOMEM +-#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ +- IORESOURCE_MEM) +-#endif +-#ifndef PCI_IRQ_RESOURCE +-#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +-#endif +-#ifndef pci_get_subvendor +-#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +-#define pci_get_subdevice(dev) ((dev)->subsystem_device) +-#endif +-#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + + #ifndef PREPARE_FUNC + #define PREPARE_FUNC(dev) (dev->prepare) +@@ -403,39 +271,21 @@ + #endif + + +-static inline int serial_paranoia_check(struct async_struct *info, +- kdev_t device, const char *routine) +-{ +-#ifdef SERIAL_PARANOIA_CHECK +- static const char *badmagic = +- "Warning: bad magic number for serial struct (%s) in %s\n"; +- static const char *badinfo = +- "Warning: null async_struct for (%s) in %s\n"; +- +- if (!info) { +- printk(badinfo, kdevname(device), routine); +- return 1; +- } +- if (info->magic != SERIAL_MAGIC) { +- printk(badmagic, kdevname(device), routine); +- return 1; +- } +-#endif +- return 0; +-} +- + static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) + { ++ unsigned int value; + switch (info->io_type) { +-#ifdef CONFIG_HUB6 +- case SERIAL_IO_HUB6: +- outb(info->hub6 - 1 + offset, info->port); +- return inb(info->port+1); +-#endif + case SERIAL_IO_MEM: +- return readb((unsigned long) info->iomem_base + ++ value = readb((unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); ++ udelay(10); ++ if (show_io) printk("in %02x = %02x\n", offset, value); ++ return value; + case SERIAL_IO_MEM32: ++ value = readl((unsigned long) info->iomem_base + ++ (offset<iomem_reg_shift)); ++ if (show_io) printk("in %02x = %02x\n", offset, value); ++ return value; + return readl((unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + default: +@@ -447,17 +297,14 @@ + int value) + { + switch (info->io_type) { +-#ifdef CONFIG_HUB6 +- case SERIAL_IO_HUB6: +- outb(info->hub6 - 1 + offset, info->port); +- outb(value, info->port+1); +- break; +-#endif + case SERIAL_IO_MEM: ++ if (show_io) printk("out %02x, %02x\n", offset, value); + writeb(value, (unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); ++ udelay(10); + break; + case SERIAL_IO_MEM32: ++ if (show_io) printk("out %02x, %02x\n", offset, value); + writel(value, (unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + break; +@@ -509,9 +356,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_stop")) +- return; +- + save_flags(flags); cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; +@@ -529,9 +373,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_start")) +- return; +- + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf +@@ -689,11 +530,7 @@ + #endif + *status = serial_inp(info, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); +-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ + tty_flip_buffer_push(tty); +-#else +- queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +-#endif + } + + static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +@@ -758,11 +595,6 @@ + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +-#ifdef CONFIG_HARD_PPS +- if ((info->flags & ASYNC_HARDPPS_CD) && +- (status & UART_MSR_DCD)) +- hardpps(); +-#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; +@@ -810,120 +642,23 @@ + } + } + +-#ifdef CONFIG_SERIAL_SHARE_IRQ +-/* +- * This is the serial driver's generic interrupt routine +- */ +-static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +-{ +- int status, iir; +- struct async_struct * info; +- int pass_counter = 0; +- struct async_struct *end_mark = 0; +-#ifdef CONFIG_SERIAL_MULTIPORT +- int first_multi = 0; +- struct rs_multiport_struct *multi; +-#endif +- +-#ifdef SERIAL_DEBUG_INTR +- printk("rs_interrupt(%d)...", irq); +-#endif +- +- info = IRQ_ports[irq]; +- if (!info) +- return; +- +-#ifdef CONFIG_SERIAL_MULTIPORT +- multi = &rs_multiport[irq]; +- if (multi->port_monitor) +- first_multi = inb(multi->port_monitor); +-#endif +- +- do { +- if (!info->tty || +- ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) { +- if (!end_mark) +- end_mark = info; +- goto next; +- } +-#ifdef SERIAL_DEBUG_INTR +- printk("IIR = %x...", serial_in(info, UART_IIR)); +-#endif +- end_mark = 0; +- +- info->last_active = jiffies; +- +- status = serial_inp(info, UART_LSR); +-#ifdef SERIAL_DEBUG_INTR +- printk("status = %x...", status); +-#endif +- if (status & UART_LSR_DR) +- receive_chars(info, &status, regs); +- check_modem_status(info); +-#ifdef CONFIG_MELAN +- if ((status & UART_LSR_THRE) || +- /* for buggy ELAN processors */ +- ((iir & UART_IIR_ID) == UART_IIR_THRI)) +- transmit_chars(info, 0); +-#else +- if (status & UART_LSR_THRE) +- transmit_chars(info, 0); +-#endif +- +- next: +- info = info->next_port; +- if (!info) { +- info = IRQ_ports[irq]; +- if (pass_counter++ > RS_ISR_PASS_LIMIT) { +-#if 0 +- printk("rs loop break\n"); +-#endif +- break; /* Prevent infinite loops */ +- } +- continue; +- } +- } while (end_mark != info); +-#ifdef CONFIG_SERIAL_MULTIPORT +- if (multi->port_monitor) +- printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", +- info->state->irq, first_multi, +- inb(multi->port_monitor)); +-#endif +-#ifdef SERIAL_DEBUG_INTR +- printk("end.\n"); +-#endif +-} +-#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ +- + + /* + * This is the serial driver's interrupt routine for a single port + */ + static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) + { +- int status, iir; ++ int status; + int pass_counter = 0; + struct async_struct * info; +-#ifdef CONFIG_SERIAL_MULTIPORT +- int first_multi = 0; +- struct rs_multiport_struct *multi; +-#endif + + #ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); + #endif +- + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +-#ifdef CONFIG_SERIAL_MULTIPORT +- multi = &rs_multiport[irq]; +- if (multi->port_monitor) +- first_multi = inb(multi->port_monitor); +-#endif +- +- iir = serial_in(info, UART_IIR); + do { + status = serial_inp(info, UART_LSR); + #ifdef SERIAL_DEBUG_INTR +@@ -932,120 +667,23 @@ + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); +- if ((status & UART_LSR_THRE) || +- /* For buggy ELAN processors */ +- ((iir & UART_IIR_ID) == UART_IIR_THRI)) ++ if (status & UART_LSR_THRE) + transmit_chars(info, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +-#if SERIAL_DEBUG_INTR ++#if 0 + printk("rs_single loop break.\n"); + #endif + break; + } +- iir = serial_in(info, UART_IIR); +-#ifdef SERIAL_DEBUG_INTR +- printk("IIR = %x...", iir); +-#endif +- } while ((iir & UART_IIR_NO_INT) == 0); +- info->last_active = jiffies; +-#ifdef CONFIG_SERIAL_MULTIPORT +- if (multi->port_monitor) +- printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", +- info->state->irq, first_multi, +- inb(multi->port_monitor)); +-#endif +-#ifdef SERIAL_DEBUG_INTR +- printk("end.\n"); +-#endif +-} +- +-#ifdef CONFIG_SERIAL_MULTIPORT +-/* +- * This is the serial driver's for multiport boards +- */ +-static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) +-{ +- int status; +- struct async_struct * info; +- int pass_counter = 0; +- int first_multi= 0; +- struct rs_multiport_struct *multi; +- + #ifdef SERIAL_DEBUG_INTR +- printk("rs_interrupt_multi(%d)...", irq); ++ printk("IIR = %x...", serial_in(info, UART_IIR)); + #endif +- +- info = IRQ_ports[irq]; +- if (!info) +- return; +- multi = &rs_multiport[irq]; +- if (!multi->port1) { +- /* Should never happen */ +- printk("rs_interrupt_multi: NULL port1!\n"); +- return; +- } +- if (multi->port_monitor) +- first_multi = inb(multi->port_monitor); +- +- while (1) { +- if (!info->tty || +- (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) +- goto next; +- ++ } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +- +- status = serial_inp(info, UART_LSR); +-#ifdef SERIAL_DEBUG_INTR +- printk("status = %x...", status); +-#endif +- if (status & UART_LSR_DR) +- receive_chars(info, &status, regs); +- check_modem_status(info); +- if (status & UART_LSR_THRE) +- transmit_chars(info, 0); +- +- next: +- info = info->next_port; +- if (info) +- continue; +- +- info = IRQ_ports[irq]; +- /* +- * The user was a bonehead, and misconfigured their +- * multiport info. Rather than lock up the kernel +- * in an infinite loop, if we loop too many times, +- * print a message and break out of the loop. +- */ +- if (pass_counter++ > RS_ISR_PASS_LIMIT) { +- printk("Misconfigured multiport serial info " +- "for irq %d. Breaking out irq loop\n", irq); +- break; +- } +- if (multi->port_monitor) +- printk("rs port monitor irq %d: 0x%x, 0x%x\n", +- info->state->irq, first_multi, +- inb(multi->port_monitor)); +- if ((inb(multi->port1) & multi->mask1) != multi->match1) +- continue; +- if (!multi->port2) +- break; +- if ((inb(multi->port2) & multi->mask2) != multi->match2) +- continue; +- if (!multi->port3) +- break; +- if ((inb(multi->port3) & multi->mask3) != multi->match3) +- continue; +- if (!multi->port4) +- break; +- if ((inb(multi->port4) & multi->mask4) != multi->match4) +- continue; +- break; +- } + #ifdef SERIAL_DEBUG_INTR + printk("end.\n"); + #endif + } +-#endif + + /* + * ------------------------------------------------------------------- +@@ -1107,22 +745,6 @@ + if (!info) + continue; + save_flags(flags); cli(); +-#ifdef CONFIG_SERIAL_SHARE_IRQ +- if (info->next_port) { +- do { +- serial_out(info, UART_IER, 0); +- info->IER |= UART_IER_THRI; +- serial_out(info, UART_IER, info->IER); +- info = info->next_port; +- } while (info); +-#ifdef CONFIG_SERIAL_MULTIPORT +- if (rs_multiport[i].port1) +- rs_interrupt_multi(i, NULL, NULL); +- else +-#endif +- rs_interrupt(i, NULL, NULL); +- } else +-#endif /* CONFIG_SERIAL_SHARE_IRQ */ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } +@@ -1132,11 +754,7 @@ + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +-#ifdef CONFIG_SERIAL_SHARE_IRQ +- rs_interrupt(0, NULL, NULL); +-#else + rs_interrupt_single(0, NULL, NULL); +-#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); +@@ -1177,50 +795,6 @@ + IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; + } + +-#ifdef CONFIG_SERIAL_RSA +-/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +-static int enable_rsa(struct async_struct *info) +-{ +- unsigned char mode; +- int result; +- unsigned long flags; +- +- save_flags(flags); cli(); +- mode = serial_inp(info, UART_RSA_MSR); +- result = mode & UART_RSA_MSR_FIFO; +- +- if (!result) { +- serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); +- mode = serial_inp(info, UART_RSA_MSR); +- result = mode & UART_RSA_MSR_FIFO; +- } +- +- restore_flags(flags); +- return result; +-} +- +-/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +-static int disable_rsa(struct async_struct *info) +-{ +- unsigned char mode; +- int result; +- unsigned long flags; +- +- save_flags(flags); cli(); +- mode = serial_inp(info, UART_RSA_MSR); +- result = !(mode & UART_RSA_MSR_FIFO); +- +- if (!result) { +- serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); +- mode = serial_inp(info, UART_RSA_MSR); +- result = !(mode & UART_RSA_MSR_FIFO); +- } +- +- restore_flags(flags); +- return result; +-} +-#endif /* CONFIG_SERIAL_RSA */ +- + static int startup(struct async_struct * info) + { + unsigned long flags; +@@ -1228,9 +802,6 @@ + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +-#ifdef CONFIG_SERIAL_MANY_PORTS +- unsigned short ICP; +-#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) +@@ -1258,6 +829,22 @@ + printk("starting up ttys%d (irq %d)...", info->line, state->irq); + #endif + ++ // Special handling to give power to devices ++ switch (info->line) { ++ case 3: ++ //printk("gsm on\n"); ++ RAMSES_GSM_ON(); ++ break; ++ case 4: ++ //printk("uart on\n"); ++ RAMSES_UART_ON(); ++ break; ++ case 5: ++ //printk("scanner on\n"); ++ RAMSES_SCANNER_ON(); ++ break; ++ } ++ + if (uart_config[state->type].flags & UART_STARTECH) { + /* Wake up UART */ + serial_outp(info, UART_LCR, 0xBF); +@@ -1305,25 +892,12 @@ + serial_outp(info, UART_LCR, 0); + } + +-#ifdef CONFIG_SERIAL_RSA +- /* +- * If this is an RSA port, see if we can kick it up to the +- * higher speed clock. +- */ +- if (state->type == PORT_RSA) { +- if (state->baud_base != SERIAL_RSA_BAUD_BASE && +- enable_rsa(info)) +- state->baud_base = SERIAL_RSA_BAUD_BASE; +- if (state->baud_base == SERIAL_RSA_BAUD_BASE) +- serial_outp(info, UART_RSA_FRR, 0); +- } +-#endif +- + #ifdef CONFIG_ARCH_PXA + if (state->type == PORT_PXA) { + switch ((long)state->iomem_base) { + case (long)&FFUART: CKEN |= CKEN6_FFUART; break; + case (long)&BTUART: CKEN |= CKEN7_BTUART; break; ++ //HS TODO: cerf keeps the clock on + case (long)&STUART: CKEN |= CKEN5_STUART; break; + } + } +@@ -1344,6 +918,7 @@ + /* + * Clear the interrupt registers. + */ ++ (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_LSR); + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); +@@ -1371,18 +946,8 @@ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +-#ifdef CONFIG_SERIAL_SHARE_IRQ +- free_irq(state->irq, &IRQ_ports[state->irq]); +-#ifdef CONFIG_SERIAL_MULTIPORT +- if (rs_multiport[state->irq].port1) +- handler = rs_interrupt_multi; +- else +-#endif +- handler = rs_interrupt; +-#else + retval = -EBUSY; + goto errout; +-#endif /* CONFIG_SERIAL_SHARE_IRQ */ + } else + handler = rs_interrupt_single; + +@@ -1417,12 +982,6 @@ + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +-#ifdef CONFIG_SERIAL_MANY_PORTS +- if (info->flags & ASYNC_FOURPORT) { +- if (state->irq == 0) +- info->MCR |= UART_MCR_OUT1; +- } else +-#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; +@@ -1437,18 +996,9 @@ + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + if (pxa_port(state->type)) +- info->IER |= UART_IER_UUE | UART_IER_RTOIE; ++ info->IER |= UART_IER_UUE | UART_IER_RTOIE; //HS TODO: UART_IER_THRI for PXA uarts? + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ + +-#ifdef CONFIG_SERIAL_MANY_PORTS +- if (info->flags & ASYNC_FOURPORT) { +- /* Enable interrupts on the AST Fourport board */ +- ICP = (info->port & 0xFE0) | 0x01F; +- outb_p(0x80, ICP); +- (void) inb_p(ICP); +- } +-#endif +- + /* + * And clear the interrupt registers again for luck. + */ +@@ -1469,7 +1019,6 @@ + /* + * Set up the tty->alt_speed kludge + */ +-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; +@@ -1480,7 +1029,6 @@ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +-#endif + + /* + * and set the speed of the serial port +@@ -1516,6 +1064,30 @@ + state->irq); + #endif + ++ switch (info->line) { ++ case 3: ++ if (ramses_stay_on & RAMSES_CONTROL_GSM_PWR) { ++ //printk("gsm on\n"); ++ RAMSES_GSM_OFF(); ++ } ++ //else printk("gsm stays on\n"); ++ break; ++ case 4: ++ if (ramses_stay_on & RAMSES_CONTROL_UART_PWR) { ++ //printk("uart off\n"); ++ RAMSES_UART_OFF(); ++ } ++ //else printk("uart stays on\n"); ++ break; ++ case 5: ++ if (ramses_stay_on & RAMSES_CONTROL_SCANNER_PWR) { ++ //printk("scanner off\n"); ++ RAMSES_SCANNER_OFF(); ++ } ++ //else printk("scanner on\n"); ++ break; ++ } ++ + save_flags(flags); cli(); /* Disable interrupts */ + + /* +@@ -1561,13 +1133,6 @@ + + info->IER = 0; + serial_outp(info, UART_IER, 0x00); /* disable all intrs */ +-#ifdef CONFIG_SERIAL_MANY_PORTS +- if (info->flags & ASYNC_FOURPORT) { +- /* reset interrupts on the AST Fourport board */ +- (void) inb((info->port & 0xFE0) | 0x01F); +- info->MCR |= UART_MCR_OUT1; +- } else +-#endif + info->MCR &= ~UART_MCR_OUT2; + if (pxa_buggy_port(state->type)) + info->MCR ^= UART_MCR_OUT2; +@@ -1586,16 +1151,6 @@ + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + +-#ifdef CONFIG_SERIAL_RSA +- /* +- * Reset the RSA board back to 115kbps compat mode. +- */ +- if ((state->type == PORT_RSA) && +- (state->baud_base == SERIAL_RSA_BAUD_BASE && +- disable_rsa(info))) +- state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +-#endif +- + #ifdef CONFIG_ARCH_PXA + if (state->type == PORT_PXA + #ifdef CONFIG_SERIAL_CONSOLE +@@ -1634,37 +1189,6 @@ + restore_flags(flags); + } + +-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +-static int baud_table[] = { +- 0, 50, 75, 110, 134, 150, 200, 300, +- 600, 1200, 1800, 2400, 4800, 9600, 19200, +- 38400, 57600, 115200, 230400, 460800, 0 }; +- +-static int tty_get_baud_rate(struct tty_struct *tty) +-{ +- struct async_struct * info = (struct async_struct *)tty->driver_data; +- unsigned int cflag, i; +- +- cflag = tty->termios->c_cflag; +- +- i = cflag & CBAUD; +- if (i & CBAUDEX) { +- i &= ~CBAUDEX; +- if (i < 1 || i > 2) +- tty->termios->c_cflag &= ~CBAUDEX; +- else +- i += 15; +- } +- if (i == 15) { +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) +- i += 1; +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) +- i += 2; +- } +- return baud_table[i]; +-} +-#endif +- + /* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. +@@ -1711,12 +1235,6 @@ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ +-#ifdef CONFIG_SERIAL_RSA +- if ((info->state->type == PORT_RSA) && +- (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && +- enable_rsa(info)) +- info->state->baud_base = SERIAL_RSA_BAUD_BASE; +-#endif + baud_base = info->state->baud_base; + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) +@@ -1778,10 +1296,6 @@ + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +-#ifdef CONFIG_SERIAL_RSA +- else if (info->state->type == PORT_RSA) +- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +-#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } +@@ -1864,9 +1378,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_put_char")) +- return; +- + if (!tty || !info->xmit.buf) + return; + +@@ -1888,9 +1399,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) +- return; +- + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped +@@ -1900,8 +1408,6 @@ + save_flags(flags); cli(); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +- if (pxa_buggy_port(info->state->type)) +- rs_interrupt_single(info->state->irq, NULL, NULL); + restore_flags(flags); + } + +@@ -1912,9 +1418,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_write")) +- return 0; +- + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + +@@ -1978,11 +1481,6 @@ + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +- if (pxa_buggy_port(info->state->type)) { +- save_flags(flags); cli(); +- rs_interrupt_single(info->state->irq, NULL, NULL); +- restore_flags(flags); +- } + } + return ret; + } +@@ -1991,8 +1489,6 @@ + { + struct async_struct *info = (struct async_struct *)tty->driver_data; + +- if (serial_paranoia_check(info, tty->device, "rs_write_room")) +- return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + } + +@@ -2000,8 +1496,6 @@ + { + struct async_struct *info = (struct async_struct *)tty->driver_data; + +- if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) +- return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + } + +@@ -2010,8 +1504,6 @@ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) +- return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); +@@ -2032,16 +1524,11 @@ + { + struct async_struct *info = (struct async_struct *)tty->driver_data; + +- if (serial_paranoia_check(info, tty->device, "rs_send_char")) +- return; +- + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +- if (pxa_buggy_port(info->state->type)) +- rs_interrupt_single(info->state->irq, NULL, NULL); + } + } + +@@ -2064,9 +1551,6 @@ + tty->ldisc.chars_in_buffer(tty)); + #endif + +- if (serial_paranoia_check(info, tty->device, "rs_throttle")) +- return; +- + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); + +@@ -2089,9 +1573,6 @@ + tty->ldisc.chars_in_buffer(tty)); + #endif + +- if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) +- return; +- + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; +@@ -2134,7 +1615,6 @@ + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; +- tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; +@@ -2160,8 +1640,7 @@ + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; +- change_port = (new_port != ((int) state->port)) || +- (new_serial.hub6 != state->hub6); ++ change_port = (new_port != ((int) state->port)); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || +@@ -2198,7 +1677,6 @@ + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && +- (rs_table[i].io_type == SERIAL_IO_PORT) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; +@@ -2220,18 +1698,11 @@ + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +-#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +-#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +-#ifdef CONFIG_SERIAL_RSA +- if (old_state.type == PORT_RSA) +- release_region(state->port + UART_RSA_BASE, 16); +- else +-#endif + release_region(state->port,8); + } + state->type = new_serial.type; +@@ -2243,31 +1714,19 @@ + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; +- info->hub6 = state->hub6 = new_serial.hub6; +- if (info->hub6) +- info->io_type = state->io_type = SERIAL_IO_HUB6; +- else if (info->io_type == SERIAL_IO_HUB6) +- info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +-#ifdef CONFIG_SERIAL_RSA +- if (state->type == PORT_RSA) +- request_region(state->port + UART_RSA_BASE, +- 16, "serial_rsa(set)"); +- else +-#endif + request_region(state->port,8,"serial(set)"); + } + + + check_and_exit: +- if ((!state->port && !state->iomem_base) || !state->type) ++ if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) +@@ -2276,7 +1735,6 @@ + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +-#endif + change_speed(info, 0); + } + } else +@@ -2414,60 +1872,14 @@ + return 0; + } + +-static int do_autoconfig(struct async_struct * info) +-{ +- int irq, retval; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- +- if (info->state->count > 1) +- return -EBUSY; +- +- shutdown(info); +- +- autoconfig(info->state); +- if ((info->state->flags & ASYNC_AUTO_IRQ) && +- (info->state->port != 0 || info->state->iomem_base != 0) && +- (info->state->type != PORT_UNKNOWN)) { +- irq = detect_uart_irq(info->state); +- if (irq > 0) +- info->state->irq = irq; +- } +- +- retval = startup(info); +- if (retval) +- return retval; +- return 0; +-} +- + /* + * rs_break() --- routine which turns the break handling on or off + */ +-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +-static void send_break( struct async_struct * info, int duration) +-{ +- if (!CONFIGURED_SERIAL_PORT(info)) +- return; +- current->state = TASK_INTERRUPTIBLE; +- current->timeout = jiffies + duration; +- cli(); +- info->LCR |= UART_LCR_SBC; +- serial_out(info, UART_LCR, info->LCR); +- schedule(); +- info->LCR &= ~UART_LCR_SBC; +- serial_out(info, UART_LCR, info->LCR); +- sti(); +-} +-#else + static void rs_break(struct tty_struct *tty, int break_state) + { + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +- if (serial_paranoia_check(info, tty->device, "rs_break")) +- return; +- + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); +@@ -2478,121 +1890,6 @@ + serial_out(info, UART_LCR, info->LCR); + restore_flags(flags); + } +-#endif +- +-#ifdef CONFIG_SERIAL_MULTIPORT +-static int get_multiport_struct(struct async_struct * info, +- struct serial_multiport_struct *retinfo) +-{ +- struct serial_multiport_struct ret; +- struct rs_multiport_struct *multi; +- +- multi = &rs_multiport[info->state->irq]; +- +- ret.port_monitor = multi->port_monitor; +- +- ret.port1 = multi->port1; +- ret.mask1 = multi->mask1; +- ret.match1 = multi->match1; +- +- ret.port2 = multi->port2; +- ret.mask2 = multi->mask2; +- ret.match2 = multi->match2; +- +- ret.port3 = multi->port3; +- ret.mask3 = multi->mask3; +- ret.match3 = multi->match3; +- +- ret.port4 = multi->port4; +- ret.mask4 = multi->mask4; +- ret.match4 = multi->match4; +- +- ret.irq = info->state->irq; +- +- if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) +- return -EFAULT; +- return 0; +-} +- +-static int set_multiport_struct(struct async_struct * info, +- struct serial_multiport_struct *in_multi) +-{ +- struct serial_multiport_struct new_multi; +- struct rs_multiport_struct *multi; +- struct serial_state *state; +- int was_multi, now_multi; +- int retval; +- void (*handler)(int, void *, struct pt_regs *); +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- state = info->state; +- +- if (copy_from_user(&new_multi, in_multi, +- sizeof(struct serial_multiport_struct))) +- return -EFAULT; +- +- if (new_multi.irq != state->irq || state->irq == 0 || +- !IRQ_ports[state->irq]) +- return -EINVAL; +- +- multi = &rs_multiport[state->irq]; +- was_multi = (multi->port1 != 0); +- +- multi->port_monitor = new_multi.port_monitor; +- +- if (multi->port1) +- release_region(multi->port1,1); +- multi->port1 = new_multi.port1; +- multi->mask1 = new_multi.mask1; +- multi->match1 = new_multi.match1; +- if (multi->port1) +- request_region(multi->port1,1,"serial(multiport1)"); +- +- if (multi->port2) +- release_region(multi->port2,1); +- multi->port2 = new_multi.port2; +- multi->mask2 = new_multi.mask2; +- multi->match2 = new_multi.match2; +- if (multi->port2) +- request_region(multi->port2,1,"serial(multiport2)"); +- +- if (multi->port3) +- release_region(multi->port3,1); +- multi->port3 = new_multi.port3; +- multi->mask3 = new_multi.mask3; +- multi->match3 = new_multi.match3; +- if (multi->port3) +- request_region(multi->port3,1,"serial(multiport3)"); +- +- if (multi->port4) +- release_region(multi->port4,1); +- multi->port4 = new_multi.port4; +- multi->mask4 = new_multi.mask4; +- multi->match4 = new_multi.match4; +- if (multi->port4) +- request_region(multi->port4,1,"serial(multiport4)"); +- +- now_multi = (multi->port1 != 0); +- +- if (IRQ_ports[state->irq]->next_port && +- (was_multi != now_multi)) { +- free_irq(state->irq, &IRQ_ports[state->irq]); +- if (now_multi) +- handler = rs_interrupt_multi; +- else +- handler = rs_interrupt; +- +- retval = request_irq(state->irq, handler, SA_SHIRQ, +- "serial", &IRQ_ports[state->irq]); +- if (retval) { +- printk("Couldn't reallocate serial interrupt " +- "driver!!\n"); +- } +- } +- return 0; +-} +-#endif + + static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +@@ -2601,12 +1898,6 @@ + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct icount; + unsigned long flags; +-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +- int retval, tmp; +-#endif +- +- if (serial_paranoia_check(info, tty->device, "rs_ioctl")) +- return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && +@@ -2616,45 +1907,6 @@ + } + + switch (cmd) { +-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +- case TCSBRK: /* SVID version: non-zero arg --> no break */ +- retval = tty_check_change(tty); +- if (retval) +- return retval; +- tty_wait_until_sent(tty, 0); +- if (signal_pending(current)) +- return -EINTR; +- if (!arg) { +- send_break(info, HZ/4); /* 1/4 second */ +- if (signal_pending(current)) +- return -EINTR; +- } +- return 0; +- case TCSBRKP: /* support for POSIX tcsendbreak() */ +- retval = tty_check_change(tty); +- if (retval) +- return retval; +- tty_wait_until_sent(tty, 0); +- if (signal_pending(current)) +- return -EINTR; +- send_break(info, arg ? arg*(HZ/10) : HZ/4); +- if (signal_pending(current)) +- return -EINTR; +- return 0; +- case TIOCGSOFTCAR: +- tmp = C_CLOCAL(tty) ? 1 : 0; +- if (copy_to_user((void *)arg, &tmp, sizeof(int))) +- return -EFAULT; +- return 0; +- case TIOCSSOFTCAR: +- if (copy_from_user(&tmp, (void *)arg, sizeof(int))) +- return -EFAULT; +- +- tty->termios->c_cflag = +- ((tty->termios->c_cflag & ~CLOCAL) | +- (tmp ? CLOCAL : 0)); +- return 0; +-#endif + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: +@@ -2667,9 +1919,6 @@ + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); +- case TIOCSERCONFIG: +- return do_autoconfig(info); +- + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + +@@ -2679,15 +1928,6 @@ + return -EFAULT; + return 0; + +-#ifdef CONFIG_SERIAL_MULTIPORT +- case TIOCSERGETMULTI: +- return get_multiport_struct(info, +- (struct serial_multiport_struct *) arg); +- case TIOCSERSETMULTI: +- return set_multiport_struct(info, +- (struct serial_multiport_struct *) arg); +-#endif +- + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest +@@ -2754,6 +1994,39 @@ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + ++ case TIOCSERSETMULTI: ++ switch (arg) { ++ ++ // switch devices on ++ case 2: RAMSES_LCD_BLIGHT_ON(); break; ++ case 3: RAMSES_GSM_ON(); break; ++ case 4: RAMSES_UART_ON(); break; ++ case 5: RAMSES_SCANNER_ON(); break; ++ case 7: RAMSES_SCANNER_WAKE_ON(); break; ++ case 8: RAMSES_SCANNER_TRIG_ON(); break; ++ case 9: RAMSES_GSM_RESET_ON(); break; ++ ++ // switch devices off ++ case 12: RAMSES_LCD_BLIGHT_OFF(); break; ++ case 13: RAMSES_GSM_OFF(); break; ++ case 14: RAMSES_UART_OFF(); break; ++ case 15: RAMSES_SCANNER_OFF(); break; ++ case 17: RAMSES_SCANNER_WAKE_OFF(); break; ++ case 18: RAMSES_SCANNER_TRIG_OFF(); break; ++ case 19: RAMSES_GSM_RESET_OFF(); break; ++ ++ // disable automatic poweroff on file-handle close ++ case 23: ramses_stay_on |= RAMSES_CONTROL_GSM_PWR; break; ++ case 24: ramses_stay_on |= RAMSES_CONTROL_UART_PWR; break; ++ case 25: ramses_stay_on |= RAMSES_CONTROL_SCANNER_PWR; break; ++ ++ // enable automatic poweroff on file-handle close ++ case 33: ramses_stay_on &= ~RAMSES_CONTROL_GSM_PWR; break; ++ case 34: ramses_stay_on &= ~RAMSES_CONTROL_UART_PWR; break; ++ case 35: ramses_stay_on &= ~RAMSES_CONTROL_SCANNER_PWR; break; ++ } ++ return 0; ++ + default: + return -ENOIOCTLCMD; + } +@@ -2801,18 +2074,6 @@ + tty->hw_stopped = 0; + rs_start(tty); + } +- +-#if 0 +- /* +- * No need to wake up processes in open wait, since they +- * sample the CLOCAL flag once, and don't recheck it. +- * XXX It's not clear whether the current behavior is correct +- * or not. Hence, this may change..... +- */ +- if (!(old_termios->c_cflag & CLOCAL) && +- (tty->termios->c_cflag & CLOCAL)) +- wake_up_interruptible(&info->open_wait); +-#endif + } + + /* +@@ -2831,9 +2092,6 @@ + struct serial_state *state; + unsigned long flags; + +- if (!info || serial_paranoia_check(info, tty->device, "rs_close")) +- return; +- + state = info->state; + + save_flags(flags); cli(); +@@ -2933,10 +2191,7 @@ + { + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; +- int lsr; +- +- if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) +- return; ++ int lsr, old_show_io; + + if (info->state->type == PORT_UNKNOWN) + return; +@@ -2974,9 +2229,11 @@ + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); + #endif ++ old_show_io = show_io; ++ show_io = 0; + while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +- printk("lsr = %d (jiff=%lu)...", lsr, jiffies); ++ printk("lsr = %02x (jiff=%lu)...", lsr, jiffies); + #endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); +@@ -2986,8 +2243,9 @@ + break; + } + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +- printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); ++ printk("lsr = %02x (jiff=%lu)...done\n", lsr, jiffies); + #endif ++ show_io = old_show_io; + } + + /* +@@ -2998,9 +2256,6 @@ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + +- if (serial_paranoia_check(info, tty->device, "rs_hangup")) +- return; +- + state = info->state; + + rs_flush_buffer(tty); +@@ -3036,12 +2291,8 @@ + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +-#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +-#else +- return -EAGAIN; +-#endif + } + + /* +@@ -3114,14 +2365,10 @@ + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +-#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +-#else +- retval = -EAGAIN; +-#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && +@@ -3223,16 +2470,12 @@ + } + tty->driver_data = info; + info->tty = tty; +- if (serial_paranoia_check(info, tty->device, "rs_open")) +- return -ENODEV; + + #ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); + #endif +-#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +-#endif + + /* + * This relies on lock_kernel() stuff so wants tidying for 2.5 +@@ -3254,12 +2497,8 @@ + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +-#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +-#else +- return -EAGAIN; +-#endif + } + + /* +@@ -3313,17 +2552,14 @@ + int ret; + unsigned long flags; + +- /* +- * Return zero characters for ports not claimed by driver. +- */ +- if (state->type == PORT_UNKNOWN) { +- return 0; /* ignore unused ports */ +- } +- + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, +- (state->port ? state->port : (long)state->iomem_base), +- state->irq); ++ state->port, state->irq); ++ ++ if (!state->port || (state->type == PORT_UNKNOWN)) { ++ ret += sprintf(buf+ret, "\n"); ++ return ret; ++ } + + /* + * Figure out the current RS-232 lines +@@ -3334,7 +2570,6 @@ + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; +- info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; +@@ -3389,13 +2624,13 @@ + } + + static int rs_read_proc(char *page, char **start, off_t off, int count, +- int *eof, void *data) ++ int *eof, void *data) + { + int i, len = 0, l; + off_t begin = 0; + +- len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", +- serial_version, LOCAL_VERSTRING, serial_revdate); ++ len += sprintf(page, "serial: %s\n", ++ LOCAL_VERSTRING); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; +@@ -3423,2038 +2658,63 @@ + */ + + /* +- * This routine prints out the appropriate serial driver version +- * number, and identifies which options were configured into this +- * driver. +- */ +-static char serial_options[] __initdata = +-#ifdef CONFIG_HUB6 +- " HUB-6" +-#define SERIAL_OPT +-#endif +-#ifdef CONFIG_SERIAL_MANY_PORTS +- " MANY_PORTS" +-#define SERIAL_OPT +-#endif +-#ifdef CONFIG_SERIAL_MULTIPORT +- " MULTIPORT" +-#define SERIAL_OPT +-#endif +-#ifdef CONFIG_SERIAL_SHARE_IRQ +- " SHARE_IRQ" +-#define SERIAL_OPT +-#endif +-#ifdef CONFIG_SERIAL_DETECT_IRQ +- " DETECT_IRQ" +-#define SERIAL_OPT +-#endif +-#ifdef ENABLE_SERIAL_PCI +- " SERIAL_PCI" +-#define SERIAL_OPT +-#endif +-#ifdef ENABLE_SERIAL_PNP +- " ISAPNP" +-#define SERIAL_OPT +-#endif +-#ifdef ENABLE_SERIAL_ACPI +- " SERIAL_ACPI" +-#define SERIAL_OPT +-#endif +-#ifdef SERIAL_OPT +- " enabled\n"; +-#else +- " no serial options enabled\n"; +-#endif +-#undef SERIAL_OPT +- +-static _INLINE_ void show_serial_version(void) +-{ +- printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, +- serial_version, LOCAL_VERSTRING, serial_revdate, +- serial_options); +-} +- +-/* +- * This routine detect the IRQ of a serial port by clearing OUT2 when +- * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at +- * each time, as long as no other device permanently request the IRQ. +- * If no IRQ is detected, or multiple IRQ appear, this function returns 0. +- * The variable "state" and the field "state->port" should not be null. +- */ +-static unsigned detect_uart_irq (struct serial_state * state) +-{ +- int irq; +- unsigned long irqs; +- unsigned char save_mcr, save_ier; +- struct async_struct scr_info; /* serial_{in,out} because HUB6 */ +- +-#ifdef CONFIG_SERIAL_MANY_PORTS +- unsigned char save_ICP=0; /* no warning */ +- unsigned short ICP=0; +- +- if (state->flags & ASYNC_FOURPORT) { +- ICP = (state->port & 0xFE0) | 0x01F; +- save_ICP = inb_p(ICP); +- outb_p(0x80, ICP); +- (void) inb_p(ICP); +- } +-#endif +- scr_info.magic = SERIAL_MAGIC; +- scr_info.state = state; +- scr_info.port = state->port; +- scr_info.flags = state->flags; +-#ifdef CONFIG_HUB6 +- scr_info.hub6 = state->hub6; +-#endif +- scr_info.io_type = state->io_type; +- scr_info.iomem_base = state->iomem_base; +- scr_info.iomem_reg_shift = state->iomem_reg_shift; +- +- /* forget possible initially masked and pending IRQ */ +- probe_irq_off(probe_irq_on()); +- save_mcr = serial_inp(&scr_info, UART_MCR); +- save_ier = serial_inp(&scr_info, UART_IER); +- serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); +- +- irqs = probe_irq_on(); +- serial_outp(&scr_info, UART_MCR, 0); +- udelay (10); +- if (state->flags & ASYNC_FOURPORT) { +- serial_outp(&scr_info, UART_MCR, +- UART_MCR_DTR | UART_MCR_RTS); +- } else { +- serial_outp(&scr_info, UART_MCR, +- UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); +- } +- serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ +- (void)serial_inp(&scr_info, UART_LSR); +- (void)serial_inp(&scr_info, UART_RX); +- (void)serial_inp(&scr_info, UART_IIR); +- (void)serial_inp(&scr_info, UART_MSR); +- serial_outp(&scr_info, UART_TX, 0xFF); +- udelay (20); +- irq = probe_irq_off(irqs); +- +- serial_outp(&scr_info, UART_MCR, save_mcr); +- serial_outp(&scr_info, UART_IER, save_ier); +-#ifdef CONFIG_SERIAL_MANY_PORTS +- if (state->flags & ASYNC_FOURPORT) +- outb_p(save_ICP, ICP); +-#endif +- return (irq > 0)? irq : 0; +-} +- +-/* +- * This is a quickie test to see how big the FIFO is. +- * It doesn't work at all the time, more's the pity. +- */ +-static int size_fifo(struct async_struct *info) +-{ +- unsigned char old_fcr, old_mcr, old_dll, old_dlm; +- int count; +- +- old_fcr = serial_inp(info, UART_FCR); +- old_mcr = serial_inp(info, UART_MCR); +- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | +- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); +- serial_outp(info, UART_MCR, UART_MCR_LOOP); +- serial_outp(info, UART_LCR, UART_LCR_DLAB); +- old_dll = serial_inp(info, UART_DLL); +- old_dlm = serial_inp(info, UART_DLM); +- serial_outp(info, UART_DLL, 0x01); +- serial_outp(info, UART_DLM, 0x00); +- serial_outp(info, UART_LCR, 0x03); +- for (count = 0; count < 256; count++) +- serial_outp(info, UART_TX, count); +- mdelay(20); +- for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && +- (count < 256); count++) +- serial_inp(info, UART_RX); +- serial_outp(info, UART_FCR, old_fcr); +- serial_outp(info, UART_MCR, old_mcr); +- serial_outp(info, UART_LCR, UART_LCR_DLAB); +- serial_outp(info, UART_DLL, old_dll); +- serial_outp(info, UART_DLM, old_dlm); +- +- return count; +-} +- +-/* +- * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. +- * When this function is called we know it is at least a StarTech +- * 16650 V2, but it might be one of several StarTech UARTs, or one of +- * its clones. (We treat the broken original StarTech 16650 V1 as a +- * 16550, and why not? Startech doesn't seem to even acknowledge its +- * existence.) +- * +- * What evil have men's minds wrought... +- */ +-static void autoconfig_startech_uarts(struct async_struct *info, +- struct serial_state *state, +- unsigned long flags) +-{ +- unsigned char scratch, scratch2, scratch3, scratch4; +- +- /* +- * First we check to see if it's an Oxford Semiconductor UART. +- * +- * If we have to do this here because some non-National +- * Semiconductor clone chips lock up if you try writing to the +- * LSR register (which serial_icr_read does) +- */ +- if (state->type == PORT_16550A) { +- /* +- * EFR [4] must be set else this test fails +- * +- * This shouldn't be necessary, but Mike Hudson +- * (Exoray@isys.ca) claims that it's needed for 952 +- * dual UART's (which are not recommended for new designs). +- */ +- info->ACR = 0; +- serial_out(info, UART_LCR, 0xBF); +- serial_out(info, UART_EFR, 0x10); +- serial_out(info, UART_LCR, 0x00); +- /* Check for Oxford Semiconductor 16C950 */ +- scratch = serial_icr_read(info, UART_ID1); +- scratch2 = serial_icr_read(info, UART_ID2); +- scratch3 = serial_icr_read(info, UART_ID3); +- +- if (scratch == 0x16 && scratch2 == 0xC9 && +- (scratch3 == 0x50 || scratch3 == 0x52 || +- scratch3 == 0x54)) { +- state->type = PORT_16C950; +- state->revision = serial_icr_read(info, UART_REV) | +- (scratch3 << 8); +- return; +- } +- } +- +- /* +- * We check for a XR16C850 by setting DLL and DLM to 0, and +- * then reading back DLL and DLM. If DLM reads back 0x10, +- * then the UART is a XR16C850 and the DLL contains the chip +- * revision. If DLM reads back 0x14, then the UART is a +- * XR16C854. +- * +- */ +- +- /* Save the DLL and DLM */ +- +- serial_outp(info, UART_LCR, UART_LCR_DLAB); +- scratch3 = serial_inp(info, UART_DLL); +- scratch4 = serial_inp(info, UART_DLM); +- +- serial_outp(info, UART_DLL, 0); +- serial_outp(info, UART_DLM, 0); +- scratch2 = serial_inp(info, UART_DLL); +- scratch = serial_inp(info, UART_DLM); +- serial_outp(info, UART_LCR, 0); +- +- if (scratch == 0x10 || scratch == 0x14) { +- if (scratch == 0x10) +- state->revision = scratch2; +- state->type = PORT_16850; +- return; +- } +- +- /* Restore the DLL and DLM */ +- +- serial_outp(info, UART_LCR, UART_LCR_DLAB); +- serial_outp(info, UART_DLL, scratch3); +- serial_outp(info, UART_DLM, scratch4); +- serial_outp(info, UART_LCR, 0); +- /* +- * We distinguish between the '654 and the '650 by counting +- * how many bytes are in the FIFO. I'm using this for now, +- * since that's the technique that was sent to me in the +- * serial driver update, but I'm not convinced this works. +- * I've had problems doing this in the past. -TYT +- */ +- if (size_fifo(info) == 64) +- state->type = PORT_16654; +- else +- state->type = PORT_16650V2; +-} +- +-/* +- * This routine is called by rs_init() to initialize a specific serial +- * port. It determines what type of UART chip this serial port is +- * using: 8250, 16450, 16550, 16550A. The important question is +- * whether or not this UART is a 16550A or not, since this will +- * determine whether or not we can use its FIFO features or not. +- */ +-static void autoconfig(struct serial_state * state) +-{ +- unsigned char status1, status2, scratch, scratch2, scratch3; +- unsigned char save_lcr, save_mcr; +- struct async_struct *info, scr_info; +- unsigned long flags; +- +- state->type = PORT_UNKNOWN; +- +-#ifdef SERIAL_DEBUG_AUTOCONF +- printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, +- state->port, (unsigned) state->iomem_base); +-#endif +- +- if (!CONFIGURED_SERIAL_PORT(state)) +- return; +- +- info = &scr_info; /* This is just for serial_{in,out} */ +- +- info->magic = SERIAL_MAGIC; +- info->state = state; +- info->port = state->port; +- info->flags = state->flags; +-#ifdef CONFIG_HUB6 +- info->hub6 = state->hub6; +-#endif +- info->io_type = state->io_type; +- info->iomem_base = state->iomem_base; +- info->iomem_reg_shift = state->iomem_reg_shift; +- +- save_flags(flags); cli(); +- +- if (!(state->flags & ASYNC_BUGGY_UART) && +- !state->iomem_base) { +- /* +- * Do a simple existence test first; if we fail this, +- * there's no point trying anything else. +- * +- * 0x80 is used as a nonsense port to prevent against +- * false positives due to ISA bus float. The +- * assumption is that 0x80 is a non-existent port; +- * which should be safe since include/asm/io.h also +- * makes this assumption. +- */ +- scratch = serial_inp(info, UART_IER); +- serial_outp(info, UART_IER, 0); +-#ifdef __i386__ +- outb(0xff, 0x080); +-#endif +- scratch2 = serial_inp(info, UART_IER); +- serial_outp(info, UART_IER, 0x0F); +-#ifdef __i386__ +- outb(0, 0x080); +-#endif +- scratch3 = serial_inp(info, UART_IER); +- serial_outp(info, UART_IER, scratch); +- if (scratch2 || scratch3 != 0x0F) { +-#ifdef SERIAL_DEBUG_AUTOCONF +- printk("serial: ttyS%d: simple autoconfig failed " +- "(%02x, %02x)\n", state->line, +- scratch2, scratch3); +-#endif +- restore_flags(flags); +- return; /* We failed; there's nothing here */ +- } +- } +- +- save_mcr = serial_in(info, UART_MCR); +- save_lcr = serial_in(info, UART_LCR); +- +- /* +- * Check to see if a UART is really there. Certain broken +- * internal modems based on the Rockwell chipset fail this +- * test, because they apparently don't implement the loopback +- * test mode. So this test is skipped on the COM 1 through +- * COM 4 ports. This *should* be safe, since no board +- * manufacturer would be stupid enough to design a board +- * that conflicts with COM 1-4 --- we hope! +- */ +- if (!(state->flags & ASYNC_SKIP_TEST)) { +- serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); +- status1 = serial_inp(info, UART_MSR) & 0xF0; +- serial_outp(info, UART_MCR, save_mcr); +- if (status1 != 0x90) { +-#ifdef SERIAL_DEBUG_AUTOCONF +- printk("serial: ttyS%d: no UART loopback failed\n", +- state->line); +-#endif +- restore_flags(flags); +- return; +- } +- } +- serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ +- serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ +- serial_outp(info, UART_LCR, 0); +- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); +- scratch = serial_in(info, UART_IIR) >> 6; +- switch (scratch) { +- case 0: +- state->type = PORT_16450; +- break; +- case 1: +- state->type = PORT_UNKNOWN; +- break; +- case 2: +- state->type = PORT_16550; +- break; +- case 3: +- state->type = PORT_16550A; +- break; +- } +- if (state->type == PORT_16550A) { +- /* Check for Startech UART's */ +- serial_outp(info, UART_LCR, UART_LCR_DLAB); +- if (serial_in(info, UART_EFR) == 0) { +- state->type = PORT_16650; +- } else { +- serial_outp(info, UART_LCR, 0xBF); +- if (serial_in(info, UART_EFR) == 0) +- autoconfig_startech_uarts(info, state, flags); +- } +- } +- if (state->type == PORT_16550A) { +- /* Check for TI 16750 */ +- serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); +- serial_outp(info, UART_FCR, +- UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); +- scratch = serial_in(info, UART_IIR) >> 5; +- if (scratch == 7) { +- /* +- * If this is a 16750, and not a cheap UART +- * clone, then it should only go into 64 byte +- * mode if the UART_FCR7_64BYTE bit was set +- * while UART_LCR_DLAB was latched. +- */ +- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); +- serial_outp(info, UART_LCR, 0); +- serial_outp(info, UART_FCR, +- UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); +- scratch = serial_in(info, UART_IIR) >> 5; +- if (scratch == 6) +- state->type = PORT_16750; +- } +- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); +- } +-#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) +- if (state->type == PORT_16550A) { +- int i; +- +- for (i = 0 ; i < PORT_RSA_MAX ; ++i) { +- if (!probe_rsa[i] && !force_rsa[i]) +- break; +- if (((probe_rsa[i] != state->port) || +- check_region(state->port + UART_RSA_BASE, 16)) && +- (force_rsa[i] != state->port)) +- continue; +- if (!enable_rsa(info)) +- continue; +- state->type = PORT_RSA; +- state->baud_base = SERIAL_RSA_BAUD_BASE; +- break; +- } +- } +-#endif +- serial_outp(info, UART_LCR, save_lcr); +- if (state->type == PORT_16450) { +- scratch = serial_in(info, UART_SCR); +- serial_outp(info, UART_SCR, 0xa5); +- status1 = serial_in(info, UART_SCR); +- serial_outp(info, UART_SCR, 0x5a); +- status2 = serial_in(info, UART_SCR); +- serial_outp(info, UART_SCR, scratch); +- +- if ((status1 != 0xa5) || (status2 != 0x5a)) +- state->type = PORT_8250; +- } +- state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; +- +- if (state->type == PORT_UNKNOWN) { +- restore_flags(flags); +- return; +- } +- +- if (info->port) { +-#ifdef CONFIG_SERIAL_RSA +- if (state->type == PORT_RSA) +- request_region(info->port + UART_RSA_BASE, 16, +- "serial_rsa(auto)"); +- else +-#endif +- request_region(info->port,8,"serial(auto)"); +- } +- +- /* +- * Reset the UART. +- */ +-#ifdef CONFIG_SERIAL_RSA +- if (state->type == PORT_RSA) +- serial_outp(info, UART_RSA_FRR, 0); +-#endif +- serial_outp(info, UART_MCR, save_mcr); +- serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | +- UART_FCR_CLEAR_RCVR | +- UART_FCR_CLEAR_XMIT)); +- serial_outp(info, UART_FCR, 0); +- (void)serial_in(info, UART_RX); +- serial_outp(info, UART_IER, 0); +- +- restore_flags(flags); +-} +- +-int register_serial(struct serial_struct *req); +-void unregister_serial(int line); +- +-#if (LINUX_VERSION_CODE > 0x20100) +-EXPORT_SYMBOL(register_serial); +-EXPORT_SYMBOL(unregister_serial); +-#else +-static struct symbol_table serial_syms = { +-#include +- X(register_serial), +- X(unregister_serial), +-#include +-}; +-#endif +- +- +-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) +- +-static void __devinit printk_pnp_dev_id(unsigned short vendor, +- unsigned short device) +-{ +- printk("%c%c%c%x%x%x%x", +- 'A' + ((vendor >> 2) & 0x3f) - 1, +- 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, +- 'A' + ((vendor >> 8) & 0x1f) - 1, +- (device >> 4) & 0x0f, +- device & 0x0f, +- (device >> 12) & 0x0f, +- (device >> 8) & 0x0f); +-} +- +-static _INLINE_ int get_pci_port(struct pci_dev *dev, +- struct pci_board *board, +- struct serial_struct *req, +- int idx) +-{ +- unsigned long port; +- int base_idx; +- int max_port; +- int offset; +- +- base_idx = SPCI_FL_GET_BASE(board->flags); +- if (board->flags & SPCI_FL_BASE_TABLE) +- base_idx += idx; +- +- if (board->flags & SPCI_FL_REGION_SZ_CAP) { +- max_port = pci_resource_len(dev, base_idx) / 8; +- if (idx >= max_port) +- return 1; +- } +- +- offset = board->first_uart_offset; +- +- /* Timedia/SUNIX uses a mixture of BARs and offsets */ +- /* Ugh, this is ugly as all hell --- TYT */ +- if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ +- switch(idx) { +- case 0: base_idx=0; +- break; +- case 1: base_idx=0; offset=8; +- break; +- case 2: base_idx=1; +- break; +- case 3: base_idx=1; offset=8; +- break; +- case 4: /* BAR 2*/ +- case 5: /* BAR 3 */ +- case 6: /* BAR 4*/ +- case 7: base_idx=idx-2; /* BAR 5*/ +- } +- +- /* Some Titan cards are also a little weird */ +- if (dev->vendor == PCI_VENDOR_ID_TITAN && +- (dev->device == PCI_DEVICE_ID_TITAN_400L || +- dev->device == PCI_DEVICE_ID_TITAN_800L)) { +- switch (idx) { +- case 0: base_idx = 1; +- break; +- case 1: base_idx = 2; +- break; +- default: +- base_idx = 4; +- offset = 8 * (idx - 2); +- } +- +- } +- +- /* HP's Diva chip puts the 4th/5th serial port further out, and +- * some serial ports are supposed to be hidden on certain models. +- */ +- if (dev->vendor == PCI_VENDOR_ID_HP && +- dev->device == PCI_DEVICE_ID_HP_SAS) { +- switch (dev->subsystem_device) { +- case 0x104B: /* Maestro */ +- if (idx == 3) idx++; +- break; +- case 0x1282: /* Everest / Longs Peak */ +- if (idx > 0) idx++; +- if (idx > 2) idx++; +- break; +- } +- if (idx > 2) { +- offset = 0x18; +- } +- } +- +- port = pci_resource_start(dev, base_idx) + offset; +- +- if ((board->flags & SPCI_FL_BASE_TABLE) == 0) +- port += idx * (board->uart_offset ? board->uart_offset : 8); +- +- if (IS_PCI_REGION_IOPORT(dev, base_idx)) { +- req->port = port; +- if (HIGH_BITS_OFFSET) +- req->port_high = port >> HIGH_BITS_OFFSET; +- else +- req->port_high = 0; +- return 0; +- } +- req->io_type = SERIAL_IO_MEM; +- req->iomem_base = ioremap(port, board->uart_offset); +- req->iomem_reg_shift = board->reg_shift; +- req->port = 0; +- return 0; +-} +- +-static _INLINE_ int get_pci_irq(struct pci_dev *dev, +- struct pci_board *board, +- int idx) +-{ +- int base_idx; +- +- if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) +- return dev->irq; +- +- base_idx = SPCI_FL_GET_IRQBASE(board->flags); +- if (board->flags & SPCI_FL_IRQ_TABLE) +- base_idx += idx; +- +- return PCI_IRQ_RESOURCE(dev, base_idx); +-} +- +-/* +- * Common enabler code shared by both PCI and ISAPNP probes +- */ +-static void __devinit start_pci_pnp_board(struct pci_dev *dev, +- struct pci_board *board) +-{ +- int k, line; +- struct serial_struct serial_req; +- int base_baud; +- +- if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { +- printk("serial: PNP device '"); +- printk_pnp_dev_id(dev->vendor, dev->device); +- printk("' prepare failed\n"); +- return; +- } +- +- if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { +- printk("serial: PNP device '"); +- printk_pnp_dev_id(dev->vendor, dev->device); +- printk("' activate failed\n"); +- return; +- } +- +- /* +- * Run the initialization function, if any +- */ +- if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) +- return; +- +- /* +- * Register the serial board in the array if we need to +- * shutdown the board on a module unload or card removal +- */ +- if (DEACTIVATE_FUNC(dev) || board->init_fn) { +- for (k=0; k < NR_PCI_BOARDS; k++) +- if (serial_pci_board[k].dev == 0) +- break; +- if (k >= NR_PCI_BOARDS) +- return; +- serial_pci_board[k].board = *board; +- serial_pci_board[k].dev = dev; +- } +- +- base_baud = board->base_baud; +- if (!base_baud) +- base_baud = BASE_BAUD; +- memset(&serial_req, 0, sizeof(serial_req)); +- +- for (k=0; k < board->num_ports; k++) { +- serial_req.irq = get_pci_irq(dev, board, k); +- if (get_pci_port(dev, board, &serial_req, k)) +- break; +- serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; +-#ifdef SERIAL_DEBUG_PCI +- printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", +- serial_req.port, serial_req.irq, serial_req.io_type); +-#endif +- line = register_serial(&serial_req); +- if (line < 0) +- break; +- rs_table[line].baud_base = base_baud; +- rs_table[line].dev = dev; +- } +-} +-#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ +- +-#ifdef ENABLE_SERIAL_PCI +-/* +- * Some PCI serial cards using the PLX 9050 PCI interface chip require +- * that the card interrupt be explicitly enabled or disabled. This +- * seems to be mainly needed on card using the PLX which also use I/O +- * mapped memory. +- */ +-static int __devinit +-pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- u8 data, *p, irq_config; +- int pci_config; +- +- irq_config = 0x41; +- pci_config = PCI_COMMAND_MEMORY; +- if (dev->vendor == PCI_VENDOR_ID_PANACOM) +- irq_config = 0x43; +- if ((dev->vendor == PCI_VENDOR_ID_PLX) && +- (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { +- /* +- * As the megawolf cards have the int pins active +- * high, and have 2 UART chips, both ints must be +- * enabled on the 9050. Also, the UARTS are set in +- * 16450 mode by default, so we have to enable the +- * 16C950 'enhanced' mode so that we can use the deep +- * FIFOs +- */ +- irq_config = 0x5b; +- pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +- } +- +- pci_read_config_byte(dev, PCI_COMMAND, &data); +- +- if (enable) +- pci_write_config_byte(dev, PCI_COMMAND, +- data | pci_config); +- +- /* enable/disable interrupts */ +- p = ioremap(pci_resource_start(dev, 0), 0x80); +- writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); +- iounmap(p); +- +- if (!enable) +- pci_write_config_byte(dev, PCI_COMMAND, +- data & ~pci_config); +- return 0; +-} +- +- +-/* +- * SIIG serial cards have an PCI interface chip which also controls +- * the UART clocking frequency. Each UART can be clocked independently +- * (except cards equiped with 4 UARTs) and initial clocking settings +- * are stored in the EEPROM chip. It can cause problems because this +- * version of serial driver doesn't support differently clocked UART's +- * on single PCI card. To prevent this, initialization functions set +- * high frequency clocking for all UART's on given card. It is safe (I +- * hope) because it doesn't touch EEPROM settings to prevent conflicts +- * with other OSes (like M$ DOS). +- * +- * SIIG support added by Andrey Panin , 10/1999 +- * +- * There is two family of SIIG serial cards with different PCI +- * interface chip and different configuration methods: +- * - 10x cards have control registers in IO and/or memory space; +- * - 20x cards have control registers in standard PCI configuration space. +- * +- * SIIG initialization functions exported for use by parport_serial.c module. +- */ +- +-#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +-#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) +- +-int __devinit +-pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- u16 data, *p; +- +- if (!enable) return 0; +- +- p = ioremap(pci_resource_start(dev, 0), 0x80); +- +- switch (dev->device & 0xfff8) { +- case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ +- data = 0xffdf; +- break; +- case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ +- data = 0xf7ff; +- break; +- default: /* 1S1P, 4S */ +- data = 0xfffb; +- break; +- } +- +- writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); +- iounmap(p); +- return 0; +-} +-EXPORT_SYMBOL(pci_siig10x_fn); +- +-#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +-#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) +- +-int __devinit +-pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- u8 data; +- +- if (!enable) return 0; +- +- /* Change clock frequency for the first UART. */ +- pci_read_config_byte(dev, 0x6f, &data); +- pci_write_config_byte(dev, 0x6f, data & 0xef); +- +- /* If this card has 2 UART, we have to do the same with second UART. */ +- if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || +- ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { +- pci_read_config_byte(dev, 0x73, &data); +- pci_write_config_byte(dev, 0x73, data & 0xef); +- } +- return 0; +-} +-EXPORT_SYMBOL(pci_siig20x_fn); +- +-/* Added for EKF Intel i960 serial boards */ +-static int __devinit +-pci_inteli960ni_fn(struct pci_dev *dev, +- struct pci_board *board, +- int enable) +-{ +- unsigned long oldval; +- +- if (!(pci_get_subdevice(dev) & 0x1000)) +- return(-1); +- +- if (!enable) /* is there something to deinit? */ +- return(0); +- +-#ifdef SERIAL_DEBUG_PCI +- printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n", +- (unsigned long) board->subdevice); +-#endif +- /* is firmware started? */ +- pci_read_config_dword(dev, 0x44, (void*) &oldval); +- if (oldval == 0x00001000L) { /* RESET value */ +- printk(KERN_DEBUG "Local i960 firmware missing"); +- return(-1); +- } +- return(0); +-} +- +-/* +- * Timedia has an explosion of boards, and to avoid the PCI table from +- * growing *huge*, we use this function to collapse some 70 entries +- * in the PCI table into one, for sanity's and compactness's sake. +- */ +-static unsigned short timedia_single_port[] = { +- 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; +-static unsigned short timedia_dual_port[] = { +- 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, +- 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, +- 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, +- 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, +- 0xD079, 0 }; +-static unsigned short timedia_quad_port[] = { +- 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, +- 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, +- 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, +- 0xB157, 0 }; +-static unsigned short timedia_eight_port[] = { +- 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, +- 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; +-static struct timedia_struct { +- int num; +- unsigned short *ids; +-} timedia_data[] = { +- { 1, timedia_single_port }, +- { 2, timedia_dual_port }, +- { 4, timedia_quad_port }, +- { 8, timedia_eight_port }, +- { 0, 0 } +-}; +- +-static int __devinit +-pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- int i, j; +- unsigned short *ids; +- +- if (!enable) +- return 0; +- +- for (i=0; timedia_data[i].num; i++) { +- ids = timedia_data[i].ids; +- for (j=0; ids[j]; j++) { +- if (pci_get_subdevice(dev) == ids[j]) { +- board->num_ports = timedia_data[i].num; +- return 0; +- } +- } +- } +- return 0; +-} +- +-/* +- * HP's Remote Management Console. The Diva chip came in several +- * different versions. N-class, L2000 and A500 have two Diva chips, each +- * with 3 UARTs (the third UART on the second chip is unused). Superdome +- * and Keystone have one Diva chip with 3 UARTs. Some later machines have +- * one Diva chip, but it has been expanded to 5 UARTs. +- */ +-static int __devinit +-pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- if (!enable) +- return 0; +- +- switch (dev->subsystem_device) { +- case 0x1049: /* Prelude Diva 1 */ +- case 0x1223: /* Superdome */ +- case 0x1226: /* Keystone */ +- case 0x1282: /* Everest / Longs Peak */ +- board->num_ports = 3; +- break; +- case 0x104A: /* Prelude Diva 2 */ +- board->num_ports = 2; +- break; +- case 0x104B: /* Maestro */ +- board->num_ports = 4; +- break; +- case 0x1227: /* Powerbar */ +- board->num_ports = 1; +- break; +- } +- +- return 0; +-} +- +-static int __devinit +-pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) +-{ +- __set_current_state(TASK_UNINTERRUPTIBLE); +- schedule_timeout(HZ/10); +- return 0; +-} +- +-/* +- * This is the configuration table for all of the PCI serial boards +- * which we support. It is directly indexed by the pci_board_num_t enum +- * value, which is encoded in the pci_device_id PCI probe table's +- * driver_data member. +- */ +-enum pci_board_num_t { +- pbn_b0_1_115200, +- pbn_default = 0, +- +- pbn_b0_2_115200, +- pbn_b0_4_115200, +- +- pbn_b0_1_921600, +- pbn_b0_2_921600, +- pbn_b0_4_921600, +- +- pbn_b0_bt_1_115200, +- pbn_b0_bt_2_115200, +- pbn_b0_bt_1_460800, +- pbn_b0_bt_2_460800, +- pbn_b0_bt_2_921600, +- +- pbn_b1_1_115200, +- pbn_b1_2_115200, +- pbn_b1_4_115200, +- pbn_b1_8_115200, +- +- pbn_b1_2_921600, +- pbn_b1_4_921600, +- pbn_b1_8_921600, +- +- pbn_b1_2_1382400, +- pbn_b1_4_1382400, +- pbn_b1_8_1382400, +- +- pbn_b2_1_115200, +- pbn_b2_8_115200, +- pbn_b2_4_460800, +- pbn_b2_8_460800, +- pbn_b2_16_460800, +- pbn_b2_4_921600, +- pbn_b2_8_921600, +- +- pbn_b2_bt_1_115200, +- pbn_b2_bt_2_115200, +- pbn_b2_bt_4_115200, +- pbn_b2_bt_2_921600, +- +- pbn_panacom, +- pbn_panacom2, +- pbn_panacom4, +- pbn_plx_romulus, +- pbn_oxsemi, +- pbn_timedia, +- pbn_intel_i960, +- pbn_sgi_ioc3, +- pbn_hp_diva, +-#ifdef CONFIG_DDB5074 +- pbn_nec_nile4, +-#endif +-#if 0 +- pbn_dci_pccom8, +-#endif +- pbn_xircom_combo, +- +- pbn_siig10x_0, +- pbn_siig10x_1, +- pbn_siig10x_2, +- pbn_siig10x_4, +- pbn_siig20x_0, +- pbn_siig20x_2, +- pbn_siig20x_4, +- +- pbn_computone_4, +- pbn_computone_6, +- pbn_computone_8, +-}; +- +-static struct pci_board pci_boards[] __devinitdata = { +- /* +- * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, +- * Offset to get to next UART's registers, +- * Register shift to use for memory-mapped I/O, +- * Initialization function, first UART offset +- */ +- +- /* Generic serial board, pbn_b0_1_115200, pbn_default */ +- { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, +- pbn_default */ +- +- { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ +- { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ +- +- { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ +- { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ +- { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ +- +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b0_bt_2_921600 */ +- +- { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ +- { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ +- { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ +- { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ +- +- { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ +- { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ +- { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ +- +- { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ +- { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ +- { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ +- +- { SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ +- { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ +- { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ +- { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ +- { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ +- { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ +- { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ +- +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ +- +- { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ +- 0x400, 7, pci_plx9050_fn }, +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ +- 0x400, 7, pci_plx9050_fn }, +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ +- 0x400, 7, pci_plx9050_fn }, +- { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ +- 0x20, 2, pci_plx9050_fn, 0x03 }, +- /* This board uses the size of PCI Base region 0 to +- * signal now many ports are available */ +- { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ +- { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ +- 0, 0, pci_timedia_fn }, +- /* EKF addition for i960 Boards form EKF with serial port */ +- { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ +- 8<<2, 2, pci_inteli960ni_fn, 0x10000}, +- { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ +- 1, 458333, 0, 0, 0, 0x20178 }, +- { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0}, /* pbn_hp_diva */ +-#ifdef CONFIG_DDB5074 +- /* +- * NEC Vrc-5074 (Nile 4) builtin UART. +- * Conditionally compiled in since this is a motherboard device. +- */ +- { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ +- 64, 3, NULL, 0x300 }, +-#endif +-#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ +- { SPCI_FL_BASE3, 8, 115200, 8 }, +-#endif +- { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ +- 0, 0, pci_xircom_fn }, +- +- { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ +- 0, 0, pci_siig10x_fn }, +- { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ +- 0, 0, pci_siig10x_fn }, +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ +- 0, 0, pci_siig10x_fn }, +- { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ +- 0, 0, pci_siig10x_fn }, +- { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ +- 0, 0, pci_siig20x_fn }, +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ +- 0, 0, pci_siig20x_fn }, +- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ +- 0, 0, pci_siig20x_fn }, +- +- { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ +- 0x40, 2, NULL, 0x200 }, +- { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ +- 0x40, 2, NULL, 0x200 }, +- { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ +- 0x40, 2, NULL, 0x200 }, +-}; +- +-/* +- * Given a complete unknown PCI device, try to use some heuristics to +- * guess what the configuration might be, based on the pitiful PCI +- * serial specs. Returns 0 on success, 1 on failure. ++ * The serial driver boot-time initialization code! + */ +-static int __devinit serial_pci_guess_board(struct pci_dev *dev, +- struct pci_board *board) ++static int __init rs_init(void) + { +- int num_iomem = 0, num_port = 0, first_port = -1; + int i; ++ struct serial_state * state; + +- /* +- * If it is not a communications device or the programming +- * interface is greater than 6, give up. +- * +- * (Should we try to make guesses for multiport serial devices +- * later?) +- */ +- if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && +- ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || +- (dev->class & 0xff) > 6) +- return 1; +- +- for (i=0; i < 6; i++) { +- if (IS_PCI_REGION_IOPORT(dev, i)) { +- num_port++; +- if (first_port == -1) +- first_port = i; +- } +- if (IS_PCI_REGION_IOMEM(dev, i)) +- num_iomem++; +- } ++ printk("pxa & ti16c754b serial driver\n"); ++ set_GPIO_IRQ_edge(7, GPIO_RISING_EDGE); ++ set_GPIO_IRQ_edge(24, GPIO_RISING_EDGE); ++ set_GPIO_IRQ_edge(25, GPIO_RISING_EDGE); ++ set_GPIO_IRQ_edge(26, GPIO_RISING_EDGE); + +- /* +- * If there is exactly one port of 8 bytes, use it. +- */ +- if (num_port == 1 && pci_resource_len(dev, first_port) == 8) { +- board->flags = first_port; +- return 0; ++ if (!request_mem_region(RAMSES_UARTA_PHYS, 16*4, "Ramses UART A")) ++ printk(KERN_ERR "unable to reserve region\n"); ++ else { ++ ramses_uarta = ioremap_nocache(RAMSES_UARTA_PHYS, 16*4); ++ if (!ramses_uarta) ++ printk(KERN_ERR "unable to map region\n"); ++ else { ++ //printk("ramses_uarta cookie is: %08x\n", (unsigned int) ramses_uarta); ++ rs_table[3].iomem_base = ramses_uarta; + } +- +- /* +- * If there is 1 or 0 iomem regions, and exactly one port, use +- * it. +- */ +- if (num_iomem <= 1 && num_port == 1) { +- board->flags = first_port; +- return 0; + } +- return 1; +-} +- +-static int __devinit serial_init_one(struct pci_dev *dev, +- const struct pci_device_id *ent) +-{ +- struct pci_board *board, tmp; +- int rc; +- +- board = &pci_boards[ent->driver_data]; +- +- rc = pci_enable_device(dev); +- if (rc) return rc; +- +- if (ent->driver_data == pbn_default && +- serial_pci_guess_board(dev, board)) +- return -ENODEV; +- else if (serial_pci_guess_board(dev, &tmp) == 0) { +- printk(KERN_INFO "Redundant entry in serial pci_table. " +- "Please send the output of\n" +- "lspci -vv, this message (%04x,%04x,%04x,%04x)\n" +- "and the manufacturer and name of " +- "serial board or modem board\n" +- "to serial-pci-info@lists.sourceforge.net.\n", +- dev->vendor, dev->device, +- pci_get_subvendor(dev), pci_get_subdevice(dev)); +- } +- +- start_pci_pnp_board(dev, board); +- +- return 0; +-} +- +-static void __devexit serial_remove_one(struct pci_dev *dev) +-{ +- int i; +- +- /* +- * Iterate through all of the ports finding those that belong +- * to this PCI device. +- */ +- for(i = 0; i < NR_PORTS; i++) { +- if (rs_table[i].dev != dev) +- continue; +- unregister_serial(i); +- rs_table[i].dev = 0; ++ if (!request_mem_region(RAMSES_UARTB_PHYS, 16*4, "Ramses UART B")) ++ printk(KERN_ERR "unable to reserve region\n"); ++ else { ++ ramses_uartb = ioremap_nocache(RAMSES_UARTB_PHYS, 16*4); ++ if (!ramses_uartb) ++ printk(KERN_ERR "unable to map region\n"); ++ else { ++ //printk("ramses_uartb cookie is: %08x\n", (unsigned int) ramses_uartb); ++ rs_table[4].iomem_base = ramses_uartb; + } +- /* +- * Now execute any board-specific shutdown procedure +- */ +- for (i=0; i < NR_PCI_BOARDS; i++) { +- struct pci_board_inst *brd = &serial_pci_board[i]; +- +- if (serial_pci_board[i].dev != dev) +- continue; +- if (brd->board.init_fn) +- (brd->board.init_fn)(brd->dev, &brd->board, 0); +- if (DEACTIVATE_FUNC(brd->dev)) +- (DEACTIVATE_FUNC(brd->dev))(brd->dev); +- serial_pci_board[i].dev = 0; + } +-} +- +- +-static struct pci_device_id serial_pci_tbl[] __devinitdata = { +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, +- pbn_b1_8_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, +- pbn_b1_4_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, +- pbn_b1_2_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, +- pbn_b1_8_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, +- pbn_b1_4_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, +- pbn_b1_2_1382400 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, +- pbn_b1_8_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, +- pbn_b1_8_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, +- pbn_b1_4_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, +- pbn_b1_4_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, +- pbn_b1_2_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, +- pbn_b1_8_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, +- pbn_b1_8_921600 }, +- { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, +- PCI_SUBVENDOR_ID_CONNECT_TECH, +- PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, +- pbn_b1_4_921600 }, +- +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_1_115200 }, +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_2_115200 }, +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_4_115200 }, +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_2_115200 }, +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_4_115200 }, +- { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_8_115200 }, +- +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_2_115200 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_2_921600 }, +- /* VScom SPCOM800, from sl@s.pl */ +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_8_921600 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_4_921600 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_KEYSPAN, +- PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, +- pbn_panacom }, +- { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_panacom4 }, +- { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_panacom2 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIFAST, +- PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, +- pbn_b2_4_460800 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIFAST, +- PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, +- pbn_b2_8_460800 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIFAST, +- PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, +- pbn_b2_16_460800 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIFAST, +- PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, +- pbn_b2_16_460800 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIRAS, +- PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, +- pbn_b2_4_460800 }, +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +- PCI_SUBVENDOR_ID_CHASE_PCIRAS, +- PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, +- pbn_b2_8_460800 }, +- /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ +- /* (Exoray@isys.ca) */ +- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, +- 0x10b5, 0x106a, 0, 0, +- pbn_plx_romulus }, +- { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b1_4_115200 }, +- { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b1_2_115200 }, +- { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b1_8_115200 }, +- { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b1_8_115200 }, +- { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, +- PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, +- pbn_b0_4_921600 }, +- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_4_115200 }, +- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_921600 }, +- +- /* Digitan DS560-558, from jimd@esoft.com */ +- { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b1_1_115200 }, +- +- /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ +- { PCI_VENDOR_ID_USR, 0x1008, +- PCI_ANY_ID, PCI_ANY_ID, }, +- +- /* Titan Electronic cards */ +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_1_921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_2_921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_4_921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_4_921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, +- PCI_ANY_ID, PCI_ANY_ID, +- SPCI_FL_BASE1, 1, 921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, +- PCI_ANY_ID, PCI_ANY_ID, +- SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, +- /* The 400L and 800L have a custom hack in get_pci_port */ +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, +- PCI_ANY_ID, PCI_ANY_ID, +- SPCI_FL_BASE_TABLE, 4, 921600 }, +- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, +- PCI_ANY_ID, PCI_ANY_ID, +- SPCI_FL_BASE_TABLE, 8, 921600 }, +- +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_4 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_4 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig10x_4 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_0 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_2 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_4 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_4 }, +- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_siig20x_4 }, +- +- /* Computone devices submitted by Doug McNash dmcnash@computone.com */ +- { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, +- PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, +- 0, 0, pbn_computone_4 }, +- { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, +- PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, +- 0, 0, pbn_computone_8 }, +- { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, +- PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, +- 0, 0, pbn_computone_6 }, +- +- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, +- { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, +- PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, +- +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_115200 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_115200 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_115200 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_460800 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_460800 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_2_460800 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_1_115200 }, +- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b0_bt_1_460800 }, +- +- /* RAStel 2 port modem, gerg@moreton.com.au */ +- { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_bt_2_115200 }, +- +- /* EKF addition for i960 Boards form EKF with serial port */ +- { PCI_VENDOR_ID_INTEL, 0x1960, +- 0xE4BF, PCI_ANY_ID, 0, 0, +- pbn_intel_i960 }, +- +- /* Xircom Cardbus/Ethernet combos */ +- { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_xircom_combo }, +- +- /* +- * Untested PCI modems, sent in from various folks... +- */ +- +- /* Elsa Model 56K PCI Modem, from Andreas Rath */ +- { PCI_VENDOR_ID_ROCKWELL, 0x1004, +- 0x1048, 0x1500, 0, 0, +- pbn_b1_1_115200 }, +- +- { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, +- 0xFF00, 0, 0, 0, +- pbn_sgi_ioc3 }, +- +- /* HP Diva card */ +- { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_SAS, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_hp_diva }, +- { PCI_VENDOR_ID_HP, 0x1290, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_b2_1_115200 }, +- +-#ifdef CONFIG_DDB5074 +- /* +- * NEC Vrc-5074 (Nile 4) builtin UART. +- * Conditionally compiled in since this is a motherboard device. +- */ +- { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_nec_nile4 }, +-#endif +- +-#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ +- { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, +- pbn_dci_pccom8 }, +-#endif +- +- { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, +- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, +- { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, +- PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, +- { 0, } +-}; +- +-MODULE_DEVICE_TABLE(pci, serial_pci_tbl); +- +-static struct pci_driver serial_pci_driver = { +- name: "serial", +- probe: serial_init_one, +- remove: __devexit_p(serial_remove_one), +- id_table: serial_pci_tbl, +-}; +- +- +-/* +- * Query PCI space for known serial boards +- * If found, add them to the PCI device space in rs_table[] +- * +- * Accept a maximum of eight boards +- * +- */ +-static void __devinit probe_serial_pci(void) +-{ +-#ifdef SERIAL_DEBUG_PCI +- printk(KERN_DEBUG "Entered probe_serial_pci()\n"); +-#endif +- +- /* Register call PCI serial devices. Null out +- * the driver name upon failure, as a signal +- * not to attempt to unregister the driver later +- */ +- if (pci_module_init (&serial_pci_driver) != 0) +- serial_pci_driver.name = ""; +- +-#ifdef SERIAL_DEBUG_PCI +- printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); +-#endif +- return; +-} +- +-#endif /* ENABLE_SERIAL_PCI */ +- +-#ifdef ENABLE_SERIAL_PNP +- +-struct pnp_board { +- unsigned short vendor; +- unsigned short device; +-}; +- +-static struct pnp_board pnp_devices[] __devinitdata = { +- /* Archtek America Corp. */ +- /* Archtek SmartLink Modem 3334BT Plug & Play */ +- { ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, +- /* Anchor Datacomm BV */ +- /* SXPro 144 External Data Fax Modem Plug & Play */ +- { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) }, +- /* SXPro 288 External Data Fax Modem Plug & Play */ +- { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) }, +- /* Rockwell 56K ACF II Fax+Data+Voice Modem */ +- { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) }, +- /* AZT3005 PnP SOUND DEVICE */ +- { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) }, +- /* Best Data Products Inc. Smart One 336F PnP Modem */ +- { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, +- /* Boca Research */ +- /* Boca Complete Ofc Communicator 14.4 Data-FAX */ +- { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, +- /* Boca Research 33,600 ACF Modem */ +- { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) }, +- /* Boca 33.6 Kbps Internal FD34FSVD */ +- { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) }, +- /* Boca 33.6 Kbps Internal FD34FSVD */ +- { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, +- /* Best Data Products Inc. Smart One 336F PnP Modem */ +- { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, +- /* Computer Peripherals Inc */ +- /* EuroViVa CommCenter-33.6 SP PnP */ +- { ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) }, +- /* Creative Labs */ +- /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ +- { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) }, +- /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ +- { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) }, +- /* Creative */ +- /* Creative Modem Blaster Flash56 DI5601-1 */ +- { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) }, +- /* Creative Modem Blaster V.90 DI5660 */ +- { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) }, +- /* FUJITSU */ +- /* Fujitsu 33600 PnP-I2 R Plug & Play */ +- { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) }, +- /* Fujitsu FMV-FX431 Plug & Play */ +- { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) }, +- /* Fujitsu 33600 PnP-I4 R Plug & Play */ +- { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) }, +- /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ +- { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) }, +- /* Archtek America Corp. */ +- /* Archtek SmartLink Modem 3334BT Plug & Play */ +- { ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) }, +- /* Hayes */ +- /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) }, +- /* Hayes Optima 336 V.34 + FAX + Voice PnP */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) }, +- /* Hayes Optima 336B V.34 + FAX + Voice PnP */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) }, +- /* Hayes Accura 56K Ext Fax Modem PnP */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) }, +- /* Hayes Accura 56K Ext Fax Modem PnP */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) }, +- /* Hayes Accura 56K Fax Modem PnP */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) }, +- /* Hayes 288, V.34 + FAX */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) }, +- /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ +- { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) }, +- /* IBM */ +- /* IBM Thinkpad 701 Internal Modem Voice */ +- { ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) }, +- /* Intertex */ +- /* Intertex 28k8 33k6 Voice EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) }, +- /* Intertex 33k6 56k Voice EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) }, +- /* Intertex 28k8 33k6 Voice SP EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) }, +- /* Intertex 33k6 56k Voice SP EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) }, +- /* Intertex 28k8 33k6 Voice SP INT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) }, +- /* Intertex 28k8 33k6 Voice SP EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) }, +- /* Intertex 33k6 56k Voice SP EXT PnP */ +- { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) }, +- /* Kortex International */ +- /* KORTEX 28800 Externe PnP */ +- { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) }, +- /* KXPro 33.6 Vocal ASVD PnP */ +- { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) }, +- /* Lasat */ +- /* LASAT Internet 33600 PnP */ +- { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) }, +- /* Lasat Safire 560 PnP */ +- { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) }, +- /* Lasat Safire 336 PnP */ +- { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) }, +- /* Microcom, Inc. */ +- /* Microcom TravelPorte FAST V.34 Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) }, +- /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) }, +- /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) }, +- /* Microcom DeskPorte 28.8P Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) }, +- /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, +- /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, +- /* Microcom DeskPorte 28.8S Internal Plug & Play */ +- { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) }, +- /* Motorola */ +- /* Motorola BitSURFR Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) }, +- /* Motorola TA210 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) }, +- /* Motorola HMTA 200 (ISDN) Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) }, +- /* Motorola BitSURFR Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) }, +- /* Motorola Lifestyle 28.8 Internal */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) }, +- /* Motorola V.3400 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) }, +- /* Motorola Lifestyle 28.8 V.34 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) }, +- /* Motorola Power 28.8 V.34 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) }, +- /* Motorola ModemSURFR External 28.8 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) }, +- /* Motorola Premier 33.6 Desktop Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) }, +- /* Motorola VoiceSURFR 56K External PnP */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) }, +- /* Motorola ModemSURFR 56K External PnP */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) }, +- /* Motorola ModemSURFR 56K Internal PnP */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) }, +- /* Motorola ModemSURFR Internal 28.8 Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) }, +- /* Motorola Premier 33.6 Internal Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) }, +- /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) }, +- /* Motorola VoiceSURFR 56K Internal PnP */ +- { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) }, +- /* Com 1 */ +- /* Deskline K56 Phone System PnP */ +- { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) }, +- /* PC Rider K56 Phone System PnP */ +- { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) }, +- /* Pace 56 Voice Internal Plug & Play Modem */ +- { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) }, +- /* Generic */ +- /* Generic standard PC COM port */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, +- /* Generic 16550A-compatible COM port */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, +- /* Compaq 14400 Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) }, +- /* Compaq 2400/9600 Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) }, +- /* Dial-Up Networking Serial Cable between 2 PCs */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) }, +- /* Dial-Up Networking Parallel Cable between 2 PCs */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) }, +- /* Standard 9600 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) }, +- /* Standard 14400 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) }, +- /* Standard 28800 bps Modem*/ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) }, +- /* Standard Modem*/ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) }, +- /* Standard 9600 bps Modem*/ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) }, +- /* Standard 14400 bps Modem*/ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) }, +- /* Standard 28800 bps Modem*/ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) }, +- /* Standard Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) }, +- /* Standard 9600 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) }, +- /* Standard 14400 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) }, +- /* Standard 28800 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) }, +- /* Standard Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) }, +- /* Standard 9600 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) }, +- /* Standard 14400 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) }, +- /* Standard 28800 bps Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) }, +- /* Standard Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) }, +- /* Standard PCMCIA Card Modem */ +- { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) }, +- /* Rockwell */ +- /* Modular Technology */ +- /* Rockwell 33.6 DPF Internal PnP */ +- /* Modular Technology 33.6 Internal PnP */ +- { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) }, +- /* Kortex International */ +- /* KORTEX 14400 Externe PnP */ +- { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) }, +- /* Viking Components, Inc */ +- /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ +- { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) }, +- /* Rockwell */ +- /* British Telecom */ +- /* Modular Technology */ +- /* Rockwell 33.6 DPF External PnP */ +- /* BT Prologue 33.6 External PnP */ +- /* Modular Technology 33.6 External PnP */ +- { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) }, +- /* Viking 56K FAX INT */ +- { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) }, +- /* SupraExpress 28.8 Data/Fax PnP modem */ +- { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) }, +- /* SupraExpress 33.6 Data/Fax PnP modem */ +- { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) }, +- /* SupraExpress 33.6 Data/Fax PnP modem */ +- { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) }, +- /* SupraExpress 33.6 Data/Fax PnP modem */ +- { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) }, +- /* Phoebe Micro */ +- /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ +- { ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) }, +- /* Archtek America Corp. */ +- /* Archtek SmartLink Modem 3334BT Plug & Play */ +- { ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, +- /* 3Com Corp. */ +- /* Gateway Telepath IIvi 33.6 */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) }, +- /* Sportster Vi 14.4 PnP FAX Voicemail */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) }, +- /* U.S. Robotics 33.6K Voice INT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) }, +- /* U.S. Robotics 33.6K Voice EXT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) }, +- /* U.S. Robotics 33.6K Voice INT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) }, +- /* U.S. Robotics 56K Voice INT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) }, +- /* U.S. Robotics 56K Voice EXT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) }, +- /* U.S. Robotics 56K FAX INT */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) }, +- /* U.S. Robotics 56K Voice INT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) }, +- /* U.S. Robotics 56K Voice EXT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) }, +- /* U.S. Robotics 56K Voice INT PnP */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) }, +- /* U.S. Robotics 56K Message */ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) }, +- /* U.S. Robotics 56K FAX EXT PnP*/ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) }, +- /* U.S. Robotics 56K FAX INT PnP*/ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) }, +- /* U.S. Robotics 56K Voice EXT PnP*/ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) }, +- /* U.S. Robotics 56K Voice INT PnP*/ +- { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) }, +- { 0, } +-}; +- +-static inline void avoid_irq_share(struct pci_dev *dev) +-{ +- int i, map = 0x1FF8; +- struct serial_state *state = rs_table; +- struct isapnp_irq *irq; +- struct isapnp_resources *res = dev->sysdata; +- +- for (i = 0; i < NR_PORTS; i++) { +- if (state->type != PORT_UNKNOWN) +- clear_bit(state->irq, &map); +- state++; +- } +- +- for ( ; res; res = res->alt) +- for(irq = res->irq; irq; irq = irq->next) +- irq->map = map; +-} +- +-static char *modem_names[] __devinitdata = { +- "MODEM", "Modem", "modem", "FAX", "Fax", "fax", +- "56K", "56k", "K56", "33.6", "28.8", "14.4", +- "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", +- "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 +-}; +- +-static int __devinit check_name(char *name) +-{ +- char **tmp = modem_names; +- +- while (*tmp) { +- if (strstr(name, *tmp)) +- return 1; +- tmp++; ++ if (!request_mem_region(RAMSES_UARTC_PHYS, 16*4, "Ramses UART C")) ++ printk(KERN_ERR "unable to reserve region\n"); ++ else { ++ ramses_uartc = ioremap_nocache(RAMSES_UARTC_PHYS, 16*4); ++ if (!ramses_uartc) ++ printk(KERN_ERR "unable to map region\n"); ++ else { ++ //printk("ramses_uartc cookie is: %08x\n", (unsigned int) ramses_uartc); ++ rs_table[5].iomem_base = ramses_uartc; + } +- return 0; +-} +- +-static inline int check_compatible_id(struct pci_dev *dev) +-{ +- int i; +- for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) +- if ((dev->vendor_compatible[i] == +- ISAPNP_VENDOR('P', 'N', 'P')) && +- (swab16(dev->device_compatible[i]) >= 0xc000) && +- (swab16(dev->device_compatible[i]) <= 0xdfff)) +- return 0; +- return 1; +-} +- +-/* +- * Given a complete unknown ISA PnP device, try to use some heuristics to +- * detect modems. Currently use such heuristic set: +- * - dev->name or dev->bus->name must contain "modem" substring; +- * - device must have only one IO region (8 byte long) with base adress +- * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. +- * +- * Such detection looks very ugly, but can detect at least some of numerous +- * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] +- * table. +- */ +-static int _INLINE_ serial_pnp_guess_board(struct pci_dev *dev, +- struct pci_board *board) +-{ +- struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; +- struct isapnp_resources *resa; +- +- if (!(check_name(dev->name) || check_name(dev->bus->name)) && +- !(check_compatible_id(dev))) +- return 1; +- +- if (!res || res->next) +- return 1; +- +- for (resa = res->alt; resa; resa = resa->alt) { +- struct isapnp_port *port; +- for (port = res->port; port; port = port->next) +- if ((port->size == 8) && +- ((port->min == 0x2f8) || +- (port->min == 0x3f8) || +- (port->min == 0x2e8) || +- (port->min == 0x3e8))) +- return 0; + } +- +- return 1; +-} +- +-static void __devinit probe_serial_pnp(void) +-{ +- struct pci_dev *dev = NULL; +- struct pnp_board *pnp_board; +- struct pci_board board; +- +-#ifdef SERIAL_DEBUG_PNP +- printk("Entered probe_serial_pnp()\n"); +-#endif +- if (!isapnp_present()) { +-#ifdef SERIAL_DEBUG_PNP +- printk("Leaving probe_serial_pnp() (no isapnp)\n"); +-#endif +- return; ++ if (!request_mem_region(RAMSES_UARTD_PHYS, 16*4, "Ramses UART D")) ++ printk(KERN_ERR "unable to reserve region\n"); ++ else { ++ ramses_uartd = ioremap_nocache(RAMSES_UARTD_PHYS, 16*4); ++ if (!ramses_uartd) ++ printk(KERN_ERR "unable to map region\n"); ++ else { ++ //printk("ramses_uartd cookie is: %08x\n", (unsigned int) ramses_uartd); ++ rs_table[6].iomem_base = ramses_uartd; + } +- +- isapnp_for_each_dev(dev) { +- if (dev->active) +- continue; +- +- memset(&board, 0, sizeof(board)); +- board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT; +- board.num_ports = 1; +- board.base_baud = 115200; +- +- for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++) +- if ((dev->vendor == pnp_board->vendor) && +- (dev->device == pnp_board->device)) +- break; +- +- if (pnp_board->vendor) { +- /* Special case that's more efficient to hardcode */ +- if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') && +- pnp_board->device == ISAPNP_DEVICE(0x1021))) +- board.flags |= SPCI_FL_NO_SHIRQ; +- } else { +- if (serial_pnp_guess_board(dev, &board)) +- continue; + } +- +- if (board.flags & SPCI_FL_NO_SHIRQ) +- avoid_irq_share(dev); +- start_pci_pnp_board(dev, &board); +- } +- +-#ifdef SERIAL_DEBUG_PNP +- printk("Leaving probe_serial_pnp() (probe finished)\n"); +-#endif +- return; +-} +- +-#endif /* ENABLE_SERIAL_PNP */ +- +-/* +- * The serial driver boot-time initialization code! +- */ +-static int __init rs_init(void) +-{ +- int i; +- struct serial_state * state; +- + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; +@@ -5463,10 +2723,6 @@ + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +-#ifdef CONFIG_SERIAL_MULTIPORT +- memset(&rs_multiport[i], 0, +- sizeof(struct rs_multiport_struct)); +-#endif + } + #ifdef CONFIG_SERIAL_CONSOLE + /* +@@ -5480,29 +2736,25 @@ + rs_table[i].irq = 0; + } + #endif +- show_serial_version(); +- + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +-#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial"; +-#endif +-#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) ++#if defined(CONFIG_DEVFS_FS) + serial_driver.name = "tts/%d"; + #else + serial_driver.name = "ttyS"; + #endif + serial_driver.major = TTY_MAJOR; +- serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; +- serial_driver.name_base = SERIAL_DEV_OFFSET; ++ serial_driver.minor_start = 64; ++ serial_driver.name_base = 0; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = +- B9600 | CS8 | CREAD | HUPCL | CLOCAL; ++ B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; +@@ -5524,31 +2776,25 @@ + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; +-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + serial_driver.break_ctl = rs_break; +-#endif +-#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +-#endif + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; +-#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) ++#if defined(CONFIG_DEVFS_FS) + callout_driver.name = "cua/%d"; + #else + callout_driver.name = "cua"; + #endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +-#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +-#endif + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); +@@ -5569,53 +2815,23 @@ + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); +- if (state->hub6) +- state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) { + state->type = PORT_UNKNOWN; + continue; + } +-#ifdef CONFIG_MCA +- if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) +- continue; +-#endif +- if (state->flags & ASYNC_BOOT_AUTOCONF) { +- state->type = PORT_UNKNOWN; +- autoconfig(state); +- } + } + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; +- if ( (state->flags & ASYNC_BOOT_AUTOCONF) +- && (state->flags & ASYNC_AUTO_IRQ) +- && (state->port != 0 || state->iomem_base != 0)) +- state->irq = detect_uart_irq(state); +- if (state->io_type == SERIAL_IO_MEM) { +- printk(KERN_INFO"ttyS%02d%s at 0x%p (irq = %d) is a %s\n", +- state->line + SERIAL_DEV_OFFSET, +- (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", +- state->iomem_base, state->irq, +- uart_config[state->type].name); +- } +- else { +- printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", +- state->line + SERIAL_DEV_OFFSET, +- (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", +- state->port, state->irq, ++ printk(KERN_INFO"tts/%d at irq %d is a %s\n", ++ state->line, ++ state->irq, + uart_config[state->type].name); +- } + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +-#ifdef ENABLE_SERIAL_PCI +- probe_serial_pci(); +-#endif +-#ifdef ENABLE_SERIAL_PNP +- probe_serial_pnp(); +-#endif + return 0; + } + +@@ -5627,6 +2843,8 @@ + { + int i = req->line; + ++ printk("%s\n", __FUNCTION__); ++ + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; +@@ -5639,7 +2857,6 @@ + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; +- rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; +@@ -5726,7 +2943,6 @@ + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } +- autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); +@@ -5734,11 +2950,8 @@ + } + restore_flags(flags); + +- if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) +- state->irq = detect_uart_irq(state); +- + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", +- state->line + SERIAL_DEV_OFFSET, ++ state->line, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); +@@ -5746,7 +2959,7 @@ + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); +- return state->line + SERIAL_DEV_OFFSET; ++ return state->line; + } + + /** +@@ -5785,7 +2998,6 @@ + int i; + struct async_struct *info; + +- /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); +@@ -5803,41 +3015,31 @@ + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +-#ifdef CONFIG_SERIAL_RSA +- if (rs_table[i].type == PORT_RSA) +- release_region(rs_table[i].port + +- UART_RSA_BASE, 16); +- else +-#endif + release_region(rs_table[i].port, 8); + } +-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) +- if (rs_table[i].iomem_base) +- iounmap(rs_table[i].iomem_base); +-#endif +- } +-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) +- for (i=0; i < NR_PCI_BOARDS; i++) { +- struct pci_board_inst *brd = &serial_pci_board[i]; +- +- if (serial_pci_board[i].dev == 0) +- continue; +- if (brd->board.init_fn) +- (brd->board.init_fn)(brd->dev, &brd->board, 0); +- if (DEACTIVATE_FUNC(brd->dev)) +- (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +-#endif + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } + +-#ifdef ENABLE_SERIAL_PCI +- if (serial_pci_driver.name[0]) +- pci_unregister_driver (&serial_pci_driver); +-#endif ++ if (ramses_uarta) { ++ iounmap(ramses_uarta); ++ release_mem_region(RAMSES_UARTA_PHYS, 16*4); ++ } ++ if (ramses_uartb) { ++ iounmap(ramses_uartb); ++ release_mem_region(RAMSES_UARTB_PHYS, 16*4); ++ } ++ if (ramses_uartc) { ++ iounmap(ramses_uartc); ++ release_mem_region(RAMSES_UARTC_PHYS, 16*4); ++ } ++ if (ramses_uartd) { ++ iounmap(ramses_uartd); ++ release_mem_region(RAMSES_UARTD_PHYS, 16*4); ++ } + } + + module_init(rs_init); +@@ -5946,7 +3148,7 @@ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; +- int baud = 9600; ++ int baud = 115200; + int bits = 8; + int parity = 'n'; + int doflow = 0; +@@ -5954,6 +3156,8 @@ + int quot = 0; + char *s; + ++ printk("%s\n", __FUNCTION__); ++ + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; +@@ -6028,19 +3232,12 @@ + info->state = state; + info->port = state->port; + info->flags = state->flags; +-#ifdef CONFIG_HUB6 +- info->hub6 = state->hub6; +-#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +-#if defined(__powerpc__) || defined(__alpha__) +- cval >>= 8; +-#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +-#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) +@@ -6082,10 +3279,15 @@ + */ + void __init serial_console_init(void) + { ++ printk("%s\n", __FUNCTION__); ++ + register_console(&sercons); + } + #endif + ++EXPORT_SYMBOL(register_serial); ++EXPORT_SYMBOL(unregister_serial); ++ + /* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" +--- /dev/null ++++ linux-2.4.21/drivers/char/sysctl.c +@@ -0,0 +1,948 @@ ++/* ++ * /proc/sys-board - Interface to the 16 bit latch and other ++ * ramses-related hardware settings ++ * ++ * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH ++ * written by H.Schurig ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "../drivers/misc/ucb1x00.h" ++ ++//#define DEBUG ++//define CPLD_LED 1 ++//define POTI 1 ++ ++/* ++ * This is the number for the "board" entry in /proc/sys: ++ */ ++#define RAMSES_SYSCTL 1312 ++ ++/* ++ * These are the numbers for the entries in /etc/sys/board ++ */ ++enum { ++ CTL_NAME=991, ++ CTL_CPLD_VERSION, ++ CTL_BOOTLOADER_CRC, ++ CTL_LINUX_CRC, ++ CTL_LED_BLUE, ++ CTL_LED_ORANGE, ++ CTL_UART, ++ CTL_MMC, ++ CTL_POWEROFF, ++ CTL_GSM_POWER, ++ CTL_GSM_RESET, ++ CTL_SCANNER_POWER, ++ CTL_SCANNER_WAKE, ++ CTL_SCANNER_TRIG, ++ CTL_SCANNER_BEAM, ++ CTL_KEY_SCAN, ++ CTL_KEY_SUSPEND, ++ CTL_KEY_OFF, ++ CTL_USBBUS_POWER, ++ CTL_USBCHIP_POWER, ++#ifdef CPLD_LED ++ CTL_LED_CPLD, ++ CTL_LED_CPLD_RED, ++#endif ++ CTL_LCD_VCC, ++ CTL_LCD_DISPOFF, ++ CTL_LCD_BLIGHT, ++#ifdef DEBUG ++ CTL_LCD_PWM0, ++ CTL_LCD_PWM1, ++#endif ++ CTL_LCD_BRIGHTNESS, ++ CTL_LCD_CONTRAST, ++ CTL_LCD_FBTURN, ++ CTL_LCD_TYPE, ++ CTL_CONTROL_SHADOW, ++ CTL_COREVOLT, ++#ifdef POTI ++ CTL_LCD_POTI_NINC, ++ CTL_LCD_POTI_NCS, ++ CTL_LCD_POTI_UP, ++#endif ++#ifdef CONFIG_MCP_UCB1400_TS ++ CTL_ADC0, ++ CTL_ADC1, ++ CTL_ADC2, ++ CTL_ADC3, ++#else ++#error NO UCB ++#endif ++ CTL_CHG_STS, ++ CTL_WALL_IN, ++ CTL_BATT_TMP, ++ CTL_BATT_LMD, ++ CTL_BATT_VSB, ++ CTL_BATT_RCAC, ++ CTL_BATT_CACT, ++ CTL_BATT_SAE, ++ CTL_BATT_DCR, ++}; ++ ++static const char ramses_board_name[] = "ramses"; ++static int dummy_int = 0; ++static char dummy_str[80]; ++ ++ ++ ++/******************************************************************/ ++/* ADC communication */ ++/******************************************************************/ ++ ++ ++#ifdef CONFIG_MCP_UCB1400_TS ++static int adc_get(int channel) ++{ ++ int val; ++ struct ucb1x00 *ucb = ucb1x00_get(); ++ ++ ucb1x00_adc_enable(ucb); ++ val = ucb1x00_adc_read(ucb, channel, 0); ++ ucb1x00_adc_disable(ucb); ++ ++ return val; ++} ++#endif ++ ++ ++ ++static int ++ramses_sysctl_handler(ctl_table * ctl, int write, struct file *filp, ++ void *buffer, size_t * lenp) ++{ ++ int *valp = ctl->data; ++ int val; ++ int ret; ++ unsigned crc; ++ void *flash; ++ ++#ifdef DEBUG ++ printk("ramses_control_shadow: %04x\n", ramses_control_shadow); ++#endif ++ ++ // Update parameters from the real registers ++ switch (ctl->ctl_name) { ++ case CTL_CPLD_VERSION: ++ sprintf(dummy_str,"20%02ld-%02ld-%02ld.%ld\n", ++ RAMSES_CPLD_YEAR & 0xff, ++ RAMSES_CPLD_MONTH & 0xff, ++ RAMSES_CPLD_DAY & 0xff, ++ RAMSES_CPLD_REV & 0xff); ++ return proc_dostring(ctl,write,filp,buffer,lenp); ++ ++ case CTL_BOOTLOADER_CRC: ++ flash = ioremap_nocache(RAMSES_FLASH_PHYS, 0x40000); ++ crc = ether_crc_le(0x40000, flash); ++ iounmap(flash); ++ sprintf(dummy_str,"%08x", crc); ++ return proc_dostring(ctl,write,filp,buffer,lenp); ++ ++ case CTL_LINUX_CRC: ++ flash = ioremap_nocache(RAMSES_FLASH_PHYS+0x40000, 3*0x40000); ++ crc = ether_crc_le(3*0x40000, flash); ++ iounmap(flash); ++ sprintf(dummy_str,"%08x", crc); ++ return proc_dostring(ctl,write,filp,buffer,lenp); ++ ++ case CTL_LED_BLUE: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_LED_BLUE_) == 0; ++ break; ++ case CTL_LED_ORANGE: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_LED_ORANGE_) == 0; ++ break; ++ case CTL_UART: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_UART_PWR) != 0; ++ break; ++ case CTL_MMC: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_MMC_PWR) != 0; ++ break; ++ case CTL_POWEROFF: ++ *valp = 0; ++ break; ++ case CTL_GSM_POWER: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_GSM_PWR) != 0; ++ break; ++ case CTL_GSM_RESET: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_GSM_RESET) != 0; ++ break; ++ ++ case CTL_SCANNER_POWER: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_PWR) != 0; ++ break; ++ case CTL_SCANNER_WAKE: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_WAKE_) == 0; ++ break; ++ case CTL_SCANNER_TRIG: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_TRIG_) == 0; ++ break; ++ case CTL_SCANNER_BEAM: ++ *valp = ramses_flags & RAMSES_FLAGS_SCANNER_BEAM; ++ break; ++ ++ case CTL_KEY_SCAN: ++ *valp = (ramses_flags & RAMSES_FLAGS_KEY_SCAN) != 0; ++ break; ++ case CTL_KEY_SUSPEND: ++ *valp = (ramses_flags & RAMSES_FLAGS_KEY_SUSPEND) != 0; ++ break; ++ case CTL_KEY_OFF: ++ *valp = (ramses_flags & RAMSES_FLAGS_KEY_OFF) != 0; ++ break; ++ ++ case CTL_USBBUS_POWER: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_USB) != 0; ++ break; ++ case CTL_USBCHIP_POWER: ++ *valp = (RAMSES_CPLD_PERIPH_PWR & USB_HOST_PWR_EN) != 0; ++ break; ++#ifdef CPLD_LED ++ case CTL_LED_CPLD: ++ *valp = (RAMSES_CPLD_LED_CONTROL & CPLD_LED1) == 0; ++ break; ++ case CTL_LED_CPLD_RED: ++ *valp = (RAMSES_CPLD_LED_CONTROL & CPLD_LED2) == 0; ++ break; ++#endif ++ case CTL_LCD_BLIGHT: ++ *valp = (ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) != 0; ++ break; ++ case CTL_LCD_VCC: ++ *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_VCC) != 0; ++ break; ++ case CTL_LCD_DISPOFF: ++ *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_DISPOFF) != 0; ++ break; ++#ifdef DEBUG ++ case CTL_LCD_PWM0: ++ *valp = PWM_PWDUTY0; ++ break; ++ case CTL_LCD_PWM1: ++#ifdef OLDCODE ++ *valp = ramses_lcd_pwm1_shadow; ++#else ++ *valp = PWM_PWDUTY1; ++#endif ++ break; ++#endif ++ case CTL_LCD_BRIGHTNESS: ++ *valp = ramses_lcd_get_brightness(); ++ break; ++ case CTL_LCD_CONTRAST: ++ *valp = ramses_lcd_get_contrast(); ++ break; ++ case CTL_LCD_FBTURN: ++ *valp = (ramses_flags & RAMSES_FLAGS_LCD_FBTURN) != 0; ++ break; ++ case CTL_LCD_TYPE: ++ *valp = ramses_lcd_type; ++ break; ++ ++ case CTL_CONTROL_SHADOW: ++ sprintf(dummy_str,"%04x", ramses_control_shadow); ++ return proc_dostring(ctl,write,filp,buffer,lenp); ++ ++ case CTL_COREVOLT: ++ *valp = ramses_corevolt_shadow; ++ break; ++ ++#ifdef POTI ++ case CTL_LCD_POTI_NINC: ++ *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PINC) != 0; ++ break; ++ case CTL_LCD_POTI_NCS: ++ *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PCS) != 0; ++ break; ++ case CTL_LCD_POTI_UP: ++ *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PUP) != 0; ++ break; ++#endif ++ ++#ifdef CONFIG_MCP_UCB1400_TS ++ case CTL_ADC0: ++ *valp = adc_get(UCB_ADC_INP_AD0); ++ break; ++ case CTL_ADC1: ++ *valp = adc_get(UCB_ADC_INP_AD1); ++ break; ++ case CTL_ADC2: ++ *valp = adc_get(UCB_ADC_INP_AD2); ++ break; ++ case CTL_ADC3: ++ *valp = adc_get(UCB_ADC_INP_AD3); ++ break; ++#endif ++ ++ case CTL_CHG_STS: ++ *valp = (RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0; ++ break; ++ case CTL_WALL_IN: ++ *valp = (RAMSES_CPLD_MISC_STATUS & RAMSES_WALL_IN) != 0; ++ break; ++ ++ case CTL_BATT_TMP: ++ *valp = ramses_hdq_get_reg(HDQ_TMP) >> 4; ++ break; ++ case CTL_BATT_LMD: ++ *valp = ramses_hdq_get_reg(HDQ_LMD); ++ break; ++ case CTL_BATT_VSB: ++ *valp = ramses_hdq_get_reg(HDQ_VSB); ++ break; ++ case CTL_BATT_RCAC: ++ *valp = ramses_hdq_get_reg(HDQ_RCAC) & 0x7f; ++ break; ++ case CTL_BATT_CACT: ++ *valp = ramses_hdq_get_reg(HDQ_CACT); ++ break; ++ case CTL_BATT_SAE: ++ *valp = ramses_hdq_get_reg(HDQ_SAEH) << 8 | ramses_hdq_get_reg(HDQ_SAEL); ++ break; ++ case CTL_BATT_DCR: ++ *valp = ramses_hdq_get_reg(HDQ_DCR); ++ break; ++ ++ default: ++ // Just ignore unsupported parameters ++ break; ++ } ++ ++ // Save old state ++ val = *valp; ++ ++ // Perform the generic integer operation ++ if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0) ++ return (ret); ++ ++ // Write changes out to the registers ++ if (write && *valp != val) { ++ ++ val = *valp; ++ switch (ctl->ctl_name) { ++ ++ case CTL_LED_BLUE: ++ if (val) ++ RAMSES_LED_BLUE_ON() ++ else ++ RAMSES_LED_BLUE_OFF(); ++ break; ++ ++ case CTL_LED_ORANGE: ++ if (val) ++ RAMSES_LED_ORANGE_ON() ++ else ++ RAMSES_LED_ORANGE_OFF(); ++ break; ++ ++ case CTL_UART: ++ if (val) ++ RAMSES_UART_ON() ++ else ++ RAMSES_UART_OFF(); ++ break; ++ ++ case CTL_MMC: ++ if (val) ++ RAMSES_MMC_ON() ++ else ++ RAMSES_MMC_OFF(); ++ break; ++ ++ case CTL_POWEROFF: ++ if (val) ++ pm_power_off(); ++ break; ++ ++ case CTL_GSM_POWER: ++ if (val) ++ RAMSES_GSM_ON() ++ else ++ RAMSES_GSM_OFF(); ++ break; ++ ++ case CTL_GSM_RESET: ++ if (val) ++ RAMSES_GSM_RESET_ON() ++ else ++ RAMSES_GSM_RESET_OFF(); ++ break; ++ ++ case CTL_SCANNER_POWER: ++ if (val) ++ RAMSES_SCANNER_ON() ++ else ++ RAMSES_SCANNER_OFF(); ++ break; ++ ++ case CTL_SCANNER_WAKE: ++ if (val) ++ RAMSES_SCANNER_WAKE_ON() ++ else ++ RAMSES_SCANNER_WAKE_OFF(); ++ break; ++ ++ case CTL_SCANNER_TRIG: ++ if (val) ++ RAMSES_SCANNER_TRIG_ON() ++ else ++ RAMSES_SCANNER_TRIG_OFF(); ++ break; ++ ++ case CTL_SCANNER_BEAM: ++ if (val) ++ ramses_flags |= RAMSES_FLAGS_SCANNER_BEAM; ++ else ++ ramses_flags &= ~RAMSES_FLAGS_SCANNER_BEAM; ++ break; ++ ++ case CTL_KEY_SCAN: ++ if (val) ++ ramses_flags |= RAMSES_FLAGS_KEY_SCAN; ++ else ++ ramses_flags &= ~RAMSES_FLAGS_KEY_SCAN; ++ break; ++ ++ case CTL_KEY_SUSPEND: ++ if (val) ++ ramses_flags |= RAMSES_FLAGS_KEY_SUSPEND; ++ else ++ ramses_flags &= ~RAMSES_FLAGS_KEY_SUSPEND; ++ break; ++ ++ case CTL_KEY_OFF: ++ if (val) ++ ramses_flags |= RAMSES_FLAGS_KEY_OFF; ++ else ++ ramses_flags &= ~RAMSES_FLAGS_KEY_OFF; ++ break; ++ ++ case CTL_USBBUS_POWER: ++ if (val) ++ RAMSES_USB_BUS_ON() ++ else ++ RAMSES_USB_BUS_OFF(); ++ break; ++ ++ case CTL_USBCHIP_POWER: ++ if (val) ++ RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN; ++ else ++ RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN; ++ break; ++ ++#ifdef CPLD_LED ++ case CTL_LED_CPLD: ++ if (val) ++ RAMSES_CPLD_LED_CONTROL &= ~CPLD_LED1; ++ else ++ RAMSES_CPLD_LED_CONTROL |= CPLD_LED1; ++ break; ++ ++ case CTL_LED_CPLD_RED: ++ if (val) ++ RAMSES_CPLD_LED_CONTROL &= ~CPLD_LED2; ++ else ++ RAMSES_CPLD_LED_CONTROL |= CPLD_LED2; ++ break; ++#endif ++ ++ case CTL_LCD_BLIGHT: ++ if (val) ++ ramses_lcd_backlight_on(); ++ else ++ ramses_lcd_backlight_off(); ++ break; ++ ++ case CTL_LCD_VCC: ++ if (val) ++ RAMSES_CPLD_LCD |= RAMSES_LCD_VCC; ++ else ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_VCC; ++ break; ++ ++ case CTL_LCD_DISPOFF: ++ if (val) ++ RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; ++ else ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; ++ break; ++ ++#ifdef DEBUG ++ case CTL_LCD_PWM0: ++ PWM_PWDUTY0 = val; ++ break; ++ ++ case CTL_LCD_PWM1: ++#ifdef OLDCODE ++ ramses_lcd_set_pwm1(val); ++#else ++ PWM_PWDUTY1 = val; ++#endif ++ break; ++#endif ++ ++ case CTL_LCD_BRIGHTNESS: ++ ramses_lcd_set_brightness(val); ++ break; ++ ++ case CTL_LCD_CONTRAST: ++ ramses_lcd_set_contrast(val); ++ break; ++ ++ case CTL_LCD_FBTURN: ++ if (val) ++ ramses_flags |= RAMSES_FLAGS_LCD_FBTURN; ++ else ++ ramses_flags &= ~RAMSES_FLAGS_LCD_FBTURN; ++ break; ++ ++ case CTL_COREVOLT: ++ ramses_set_corevolt(val); ++ break; ++ ++#ifdef POTI ++ case CTL_LCD_POTI_NCS: ++ if (val) ++ RAMSES_CPLD_LCD |= RAMSES_LCD_PCS; ++ else ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_PCS; ++ break; ++ case CTL_LCD_POTI_NINC: ++ if (val) ++ RAMSES_CPLD_LCD |= RAMSES_LCD_PINC; ++ else ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_PINC; ++ break; ++ case CTL_LCD_POTI_UP: ++ if (val) ++ RAMSES_CPLD_LCD |= RAMSES_LCD_PUP; ++ else ++ RAMSES_CPLD_LCD &= ~RAMSES_LCD_PUP; ++ break; ++#endif ++ ++ default: ++ // Just ignore unsupported parameters ++ break; ++ } ++ } ++ ++#ifdef DEBUG ++ printk("ramses_control_shadow new: %04x\n", ramses_control_shadow); ++#endif ++ return ret; ++} ++ ++ ++ ++static ctl_table ramses_table[] = { ++ { ++ procname: "sys_name", ++ ctl_name: CTL_NAME, ++ data: &ramses_board_name, ++ maxlen: sizeof(ramses_board_name), ++ proc_handler: &proc_dostring, ++ mode: 0444, // read-only ++ }, { ++ procname: "sys_cpldver", ++ ctl_name: CTL_CPLD_VERSION, ++ data: &dummy_str, ++ maxlen: sizeof(dummy_str), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, // read-only ++ }, { ++ procname: "sys_bootcrc", ++ ctl_name: CTL_BOOTLOADER_CRC, ++ data: &dummy_str, ++ maxlen: sizeof(dummy_str), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, // read-only ++ }, { ++ procname: "sys_linuxcrc", ++ ctl_name: CTL_LINUX_CRC, ++ data: &dummy_str, ++ maxlen: sizeof(dummy_str), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, // read-only ++ }, { ++ procname: "led_blue", ++ ctl_name: CTL_LED_BLUE, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "led_orange", ++ ctl_name: CTL_LED_ORANGE, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "pwr_uart", ++ ctl_name: CTL_UART, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "pwr_mmc", ++ ctl_name: CTL_MMC, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "pwr_off", ++ ctl_name: CTL_POWEROFF, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "gsm_power", ++ ctl_name: CTL_GSM_POWER, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "gsm_reset", ++ ctl_name: CTL_GSM_RESET, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "scanner_power", ++ ctl_name: CTL_SCANNER_POWER, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "scanner_wake", ++ ctl_name: CTL_SCANNER_WAKE, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "scanner_trig", ++ ctl_name: CTL_SCANNER_TRIG, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "scanner_beam", ++ ctl_name: CTL_SCANNER_BEAM, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "key_scan", ++ ctl_name: CTL_KEY_SCAN, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "key_suspend", ++ ctl_name: CTL_KEY_SUSPEND, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "key_off", ++ ctl_name: CTL_KEY_OFF, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "usb_bus_power", ++ ctl_name: CTL_USBBUS_POWER, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "usb_chip_power", ++ ctl_name: CTL_USBCHIP_POWER, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, ++#if LED_CPLD ++ { ++ procname: "led_cpld", ++ ctl_name: CTL_LED_CPLD, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "led_cpld_red", ++ ctl_name: CTL_LED_CPLD_RED, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, ++#endif ++ { ++ procname: "lcd_backlight", ++ ctl_name: CTL_LCD_BLIGHT, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_vcc", ++ ctl_name: CTL_LCD_VCC, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_dispoff", ++ ctl_name: CTL_LCD_DISPOFF, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_brightness", ++ ctl_name: CTL_LCD_BRIGHTNESS, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_contrast", ++ ctl_name: CTL_LCD_CONTRAST, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_fbturn", ++ ctl_name: CTL_LCD_FBTURN, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_type", ++ ctl_name: CTL_LCD_TYPE, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, ++#ifdef POTI ++ { ++ procname: "lcd_poti_ncs", ++ ctl_name: CTL_LCD_POTI_NCS, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_poti_ninc", ++ ctl_name: CTL_LCD_POTI_NINC, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_poti_up", ++ ctl_name: CTL_LCD_POTI_UP, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, ++#endif ++#ifdef DEBUG ++ { ++ procname: "lcd_pwm0", ++ ctl_name: CTL_LCD_PWM0, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, { ++ procname: "lcd_pwm1", ++ ctl_name: CTL_LCD_PWM1, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, ++#endif ++ { ++ procname: "sys_shadowreg", ++ ctl_name: CTL_CONTROL_SHADOW, ++ data: &dummy_str, ++ maxlen: sizeof(dummy_str), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, ++ { ++ procname: "pwr_corevolt", ++ ctl_name: CTL_COREVOLT, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0664, ++ }, ++#ifdef CONFIG_MCP_UCB1400_TS ++ { ++ procname: "adc0_vcc", ++ ctl_name: CTL_ADC0, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "adc1_ntc", ++ ctl_name: CTL_ADC1, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "adc2_goldcap", ++ ctl_name: CTL_ADC2, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "adc3_batt", ++ ctl_name: CTL_ADC3, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, ++#else ++#error No UCB ++#endif ++ { ++ procname: "pwr_wall_in", ++ ctl_name: CTL_WALL_IN, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_charge", ++ ctl_name: CTL_CHG_STS, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_temp", ++ ctl_name: CTL_BATT_TMP, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_lmd", ++ ctl_name: CTL_BATT_LMD, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_vsb", ++ ctl_name: CTL_BATT_VSB, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_rcac", ++ ctl_name: CTL_BATT_RCAC, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_cact", ++ ctl_name: CTL_BATT_CACT, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_sae", ++ ctl_name: CTL_BATT_SAE, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, { ++ procname: "batt_dcr", ++ ctl_name: CTL_BATT_DCR, ++ data: &dummy_int, ++ maxlen: sizeof(int), ++ proc_handler: &ramses_sysctl_handler, ++ mode: 0444, ++ }, ++ ++ {0} ++ }; ++ ++static ctl_table ramses_root_table[] = { ++ {RAMSES_SYSCTL, "board", NULL, 0, 0555, ramses_table}, ++ {0} ++ }; ++ ++ ++static struct ctl_table_header *ramses_table_header; ++ ++ ++static int __init ramses_sysctl_init(void) ++{ ++ ramses_table_header = register_sysctl_table(ramses_root_table, 0); ++ if (!ramses_table_header) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void __exit ramses_sysctl_exit(void) ++{ ++ unregister_sysctl_table(ramses_table_header); ++} ++ ++ ++module_init(ramses_sysctl_init); ++module_exit(ramses_sysctl_exit); ++ ++MODULE_AUTHOR("Holger Schurig "); ++MODULE_DESCRIPTION("Implements /proc/sys/board"); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/char/vt.c~linux-vtcomparison ++++ linux-2.4.21/drivers/char/vt.c +@@ -163,7 +163,9 @@ + + if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) + return -EFAULT; +- if (i >= NR_KEYS || s >= MAX_NR_KEYMAPS) ++ if (i >= NR_KEYS) ++ return -EINVAL; ++ if (s >= MAX_NR_KEYMAPS) + return -EINVAL; + + switch (cmd) { +--- linux-2.4.21/drivers/input/Config.in~keyb-input ++++ linux-2.4.21/drivers/input/Config.in +@@ -7,6 +7,8 @@ + + tristate 'Input core support' CONFIG_INPUT + dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_INPUT ++dep_tristate ' Ramses keyboard' CONFIG_INPUT_RAMSES_KEYB $CONFIG_INPUT_KEYBDEV $CONFIG_ARCH_RAMSES ++dep_tristate ' Ramses wedge' CONFIG_INPUT_RAMSES_WEDGE $CONFIG_INPUT_RAMSES_KEYB + dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT + if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then + int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 +--- linux-2.4.21/drivers/input/Makefile~ramses-keyb ++++ linux-2.4.21/drivers/input/Makefile +@@ -8,7 +8,7 @@ + + # Objects that export symbols. + +-export-objs := input.o ++export-objs := input.o ramses_keyb.o + + # Object file lists. + +@@ -21,10 +21,12 @@ + + obj-$(CONFIG_INPUT) += input.o + obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o ++obj-$(CONFIG_INPUT_RAMSES_KEYB) += ramses_keyb.o + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o + obj-$(CONFIG_INPUT_JOYDEV) += joydev.o + obj-$(CONFIG_INPUT_EVDEV) += evdev.o + obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o ++obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o + + # The global Rules.make. + +--- /dev/null ++++ linux-2.4.21/drivers/input/ramses_cellmap.h +@@ -0,0 +1,34 @@ ++static int ramses_cellmap[][8] = { ++ { KEY_A, KEY_B, KEY_C, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 0 ++ { KEY_D, KEY_E, KEY_F, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 1 ++ { KEY_G, KEY_H, KEY_I, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 2 ++ { KEY_J, KEY_K, KEY_L, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 3 ++ { KEY_M, KEY_N, KEY_O, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 4 ++ { KEY_P, KEY_Q, KEY_R, KEY_S, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 5 ++ { KEY_T, KEY_U, KEY_V, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 6 ++ { KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 7 ++ { KEY_AE, KEY_OE, KEY_UE, KEY_SZ, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 8 ++ { KEY_sA, KEY_sB, KEY_sC, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 9 ++ { KEY_sD, KEY_sE, KEY_sF, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 10 ++ { KEY_sG, KEY_sH, KEY_sI, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 11 ++ { KEY_sJ, KEY_sK, KEY_sL, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 12 ++ { KEY_sM, KEY_sN, KEY_sO, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 13 ++ { KEY_sP, KEY_sQ, KEY_sR, KEY_sS, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 14 ++ { KEY_sT, KEY_sU, KEY_sV, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 15 ++ { KEY_sW, KEY_sX, KEY_sY, KEY_sZ, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 16 ++ { KEY_sAE, KEY_sOE, KEY_sUE, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 17 ++ { KEY_COLON, KEY_FSLASH,KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 18 ++ { KEY_SEMI, KEY_BSLASH,KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 19 ++ { KEY_COMMA, KEY_STAR, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 20 ++ { KEY_UNDERL,KEY_EQUAL, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 21 ++ { KEY_PLUS, KEY_MINUS, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 22 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 24 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 24 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 25 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 26 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 27 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 28 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 29 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 30 ++ { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 31 ++}; +--- /dev/null ++++ linux-2.4.21/drivers/input/ramses_keyb.c +@@ -0,0 +1,596 @@ ++/* ++ * Keyboard driver using input layer ++ * ++ * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH ++ * written by H.Schurig ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++// Debug ++//#define DEBUG ++//#define DEBUG_DUMP_KEYSTATE ++#ifdef DEBUG ++# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) ++# define PRINTK(fmt, args...) printk(fmt, ## args) ++#else ++# define DPRINTK(fmt, args...) ++# define PRINTK(fmt, args...) ++#endif ++ ++ ++/* ++ * Timeouts ++ */ ++#define SCANINTERVAL HZ/10 ++#define TRIGOFFINTERVAL HZ*2 ++#define CELLINTERVAL HZ+HZ/2 ++#define MAPINTERVAL 15*HZ ++#define SUSPEND_COUNTER 40 ++#define SUSPEND_LED_COUNTER 8 ++#define SUSPEND_NOW 0xffff ++#define KEYBD_MATRIX_SETTLING_TIME_US 100 ++ ++ ++/* ++ * macros for matrix keyboard driver ++ */ ++#define KEYBD_MATRIX_NUMBER_INPUTS 7 ++#define KEYBD_MATRIX_NUMBER_OUTPUTS 14 ++ ++#define KEYBD_MATRIX_SET_OUTPUTS(outputs) \ ++{\ ++ RAMSES_CPLD_KB_COL_LOW = outputs;\ ++ RAMSES_CPLD_KB_COL_HIGH = outputs >> 7;\ ++} ++ ++#define KEYBD_MATRIX_GET_INPUTS(inputs) \ ++{\ ++ inputs = (RAMSES_CPLD_KB_ROW & 0x7f);\ ++} ++ ++ ++// External functions (are they in some #include file?) ++extern int input_setkeycode(unsigned int scancode, unsigned int keycode); ++extern int input_getkeycode(unsigned int scancode); ++extern int input_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode); ++extern char input_unexpected_up(unsigned char keycode); ++extern unsigned char input_sysrq_xlate[]; ++extern int pm_suggest_suspend(void); ++ ++// Keyboard-Related definitions ++#define KEYBD_MATRIX_INPUT_MASK ((1 << KEYBD_MATRIX_NUMBER_INPUTS)-1) ++#define KEYBD_MATRIX_OUTPUT_MASK ((1 << KEYBD_MATRIX_NUMBER_OUTPUTS)-1) ++ ++#include "ramses_scancodes.h" ++#include "ramses_keymap.h" ++#include "ramses_cellmap.h" ++ ++ ++static char *kbd_name = "Keyboard"; ++struct input_dev ramses_kbd_dev; ++static struct timer_list reenable_timer; ++static struct timer_list trigoff_timer; ++static struct tq_struct tq_suspend; ++static __u16 keystate_cur[KEYBD_MATRIX_NUMBER_OUTPUTS]; ++static __u16 keystate_prev[KEYBD_MATRIX_NUMBER_OUTPUTS]; ++static __u16 keystate_keep[KEYBD_MATRIX_NUMBER_OUTPUTS]; // used for auto-repeat ++static struct timer_list cell_timer; ++static int curr_map = MAP_NORMAL; ++static int cell_key = -1; ++static int cell_sel = -1; ++static struct pm_dev *pm_keyb; ++static int suspend_counter = 0; ++ ++ ++void ramses_key(int keycode) ++{ ++ DPRINTK("keycode: %d 0x%x\n", keycode, keycode); ++ if (KVAL(keycode)) { ++ switch (KMOD(keycode)) { ++ case KM_SHIFT: ++ DPRINTK("shift\n"); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTSHIFT, 1); ++ break; ++ case KM_CTRL: ++ DPRINTK("ctrl\n"); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 1); ++ break; ++ case KM_ALT: ++ DPRINTK("alt\n"); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 1); ++ break; ++ case KM_ALTGR: ++ DPRINTK("altgr\n"); ++ input_report_key(&ramses_kbd_dev, KEY_RIGHTALT, 1); ++ break; ++ case KM_ALTCTRL: ++ DPRINTK("alt+ctrl\n"); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 1); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 1); ++ break; ++ } ++ ++ DPRINTK("report: %d 0x%x\n", KVAL(keycode), KVAL(keycode)); ++ input_report_key(&ramses_kbd_dev, KVAL(keycode), 1); ++ input_report_key(&ramses_kbd_dev, KVAL(keycode), 0); ++ ++ switch (KMOD(keycode)) { ++ case KM_SHIFT: ++ input_report_key(&ramses_kbd_dev, KEY_LEFTSHIFT, 0); ++ break; ++ case KM_CTRL: ++ input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 0); ++ break; ++ case KM_ALT: ++ input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 0); ++ break; ++ case KM_ALTGR: ++ input_report_key(&ramses_kbd_dev, KEY_RIGHTALT, 0); ++ break; ++ case KM_ALTCTRL: ++ input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 0); ++ input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 0); ++ break; ++ } ++ } ++} ++ ++static void kbd_cell_timer(unsigned long keepmap) ++{ ++ int keycode; ++ ++ if (cell_sel != -1) { ++ keycode = ramses_cellmap[cell_key][cell_sel]; ++ //DPRINTK("key: %d sel: %d keycode: %d 0x%x\n", cell_key, cell_sel, keycode, keycode); ++ ramses_key(keycode); ++ cell_sel = -1; ++ } ++ ++ if (!keepmap && curr_map!=MAP_NORMAL) { ++ DPRINTK("normal map because of %ld\n", keepmap); ++ curr_map = MAP_NORMAL; ++ RAMSES_LED_BLUE_OFF(); ++ RAMSES_LED_ORANGE_OFF(); ++ } ++} ++ ++static void kbd_setleds(void) ++{ ++ if (suspend_counter >= SUSPEND_LED_COUNTER) { ++ if (suspend_counter & 4) { ++ RAMSES_LED_ORANGE_OFF(); ++ RAMSES_LED_BLUE_ON(); ++ } else { ++ RAMSES_LED_ORANGE_ON(); ++ RAMSES_LED_BLUE_OFF(); ++ } ++ return; ++ } ++ ++ switch (curr_map) { ++ case MAP_NORMAL: ++ RAMSES_LED_BLUE_OFF(); ++ RAMSES_LED_ORANGE_OFF(); ++ return; ++ ++ case MAP_BLUE: ++ RAMSES_LED_BLUE_ON(); ++ RAMSES_LED_ORANGE_OFF(); ++ return; ++ ++ case MAP_ORANGE: ++ RAMSES_LED_BLUE_OFF(); ++ RAMSES_LED_ORANGE_ON(); ++ return; ++ ++ case MAP_CAPS: ++ RAMSES_LED_BLUE_ON(); ++ RAMSES_LED_ORANGE_ON(); ++ return; ++ } ++ DPRINTK("unknown map\n"); ++} ++ ++ ++static void kbd_start_scanner(void) ++{ ++ RAMSES_SCANNER_TRIG_OFF(); ++ RAMSES_SCANNER_WAKE_OFF(); ++ RAMSES_SCANNER_TRIG_ON(); ++ mod_timer(&trigoff_timer, jiffies + TRIGOFFINTERVAL); ++} ++ ++static void kbd_stop_scanner(unsigned long dummy) ++{ ++ RAMSES_SCANNER_TRIG_OFF(); ++} ++ ++static int kbd_dokeycode(unsigned char scancode, int down) ++{ ++ int i,keycode; ++ ++ //DPRINTK("calling with (%d,%x,%d)\n", scancode, scancode, down); ++ if (scancode >= MAX_SCANCODES) { ++ printk("%s: scancode too big for table\n", __FUNCTION__); ++ return 0; ++ } ++ ++ keycode = ramses_keymap[scancode][curr_map]; ++ ++ ++ if (keycode==KEY_SCAN) { ++ if ((ramses_flags & RAMSES_FLAGS_KEY_SCAN) == 0) ++ return 0; ++ ++ DPRINTK("scan btn\n"); ++ if (down) { ++ if (ramses_flags & RAMSES_FLAGS_SCANNER_BEAM) { ++ // just turn on laser beam ++ RAMSES_SCANNER_WAKE_ON(); ++ } else { ++ kbd_start_scanner(); ++ } ++ } else { ++ if (ramses_flags & RAMSES_FLAGS_SCANNER_BEAM) { ++ kbd_start_scanner(); ++ } else { ++ kbd_stop_scanner(0); ++ } ++ } ++ return 0; ++ } ++ ++ ++ if (keycode==KEY_SUSP) { ++ if ((ramses_flags & RAMSES_FLAGS_KEY_SUSPEND) == 0) ++ return 0; ++ ++ if (down) { ++ suspend_counter++; ++ if (suspend_counter >= SUSPEND_COUNTER) { ++ suspend_counter = SUSPEND_NOW; ++ } ++ } else { ++ if (suspend_counter == SUSPEND_NOW) { ++ curr_map = MAP_NORMAL; ++ schedule_task(&tq_suspend); ++ } ++ suspend_counter = 0; ++ } ++ return down; ++ } ++ ++ ++ if (keycode==KEY_OFF) { ++ if (down || ((ramses_flags & RAMSES_FLAGS_KEY_OFF) == 0)) ++ return 0; ++ curr_map = MAP_NORMAL; ++ ramses_shut_off(); ++ return 0; ++ } ++ ++ ++ if (!down) ++ return 0; ++ ++ ++ DPRINTK("curr_map %d scancode %d keycode %d 0x%x typ %d\n", curr_map, scancode, keycode, keycode, KMOD(keycode)); ++ ++ ++ // Cell-Phone keyboard handling ++ if (KMOD(keycode)==KM_CELL) { ++ //DPRINTK("cell phone key %d\n", KVAL(keycode)); ++ ++ // did we press a different cell-phone key as last time? ++ if (KVAL(keycode)!=cell_key) ++ kbd_cell_timer(1); ++ ++ cell_key = KVAL(keycode); // store current cell-phone key ++ cell_sel++; // increase current sub-key ++ if (ramses_cellmap[cell_key][cell_sel]==0) // if at end of sub-key list, back off ++ cell_sel = 0; ++ //DPRINTK("cell_key: %d cell_sel: %d\n", cell_key, cell_sel); ++ // auto-emit via kbd_cell_timer ++ mod_timer(&cell_timer, jiffies + CELLINTERVAL); ++ return 0; // do not revert to keys_normal ++ } ++ ++ ++ // if we pressed any other key then a cell-phone key, we look if the ++ // current half-pressed cell-phone key should be emitted ++ kbd_cell_timer(1); ++ ++ ++ switch(keycode) { ++ ++ // Keymap handling ++ ++ case KEY_NORM: ++ DPRINTK("norm key map\n"); ++ curr_map = MAP_NORMAL; ++ return 0; ++ ++ case KEY_BLUE: ++ //DPRINTK("blue key map\n"); ++ curr_map = MAP_BLUE; ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_ORNG: ++ //DPRINTK("orange key map\n"); ++ curr_map = MAP_ORANGE; ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_CAPS: ++ DPRINTK("caps key map\n"); ++ curr_map = MAP_CAPS; ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_BRIP: ++ i = ramses_lcd_get_brightness()-6; ++ ramses_lcd_set_brightness(i); ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_BRIM: ++ i = ramses_lcd_get_brightness()+6; ++ ramses_lcd_set_brightness(i); ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_CTRM: ++ i = ramses_lcd_get_contrast()+3; ++ ramses_lcd_set_contrast(i); ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ ++ case KEY_CTRP: ++ i = ramses_lcd_get_contrast()-3; ++ ramses_lcd_set_contrast(i); ++ mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap ++ return 0; ++ } ++ ++ // normal keys ++ ++ ramses_key(keycode); ++ ++ if (curr_map!=MAP_NORMAL) ++ DPRINTK("back to normal map\n"); ++ curr_map = MAP_NORMAL; ++ RAMSES_LED_BLUE_OFF(); ++ RAMSES_LED_ORANGE_OFF(); ++ return 0; ++} ++ ++ ++/** ++ * @param rescan 0 if we look for pressed keys, 1 if we look for released keys ++ * ++ * This routine get's called from the ISR (then rescan is always 0) or from ++ * the reenable_timer function (then rescan is 1). ++ */ ++static void kbd_scan_keyboard(unsigned long rescan) ++{ ++ int i,n; ++ int cols; ++ unsigned char code; ++ __u16 keystate_xor[KEYBD_MATRIX_NUMBER_OUTPUTS]; ++ ++ ++ // Find out if a key (or more) was pressed down. It's possible that ++ // because of spikes we got an interrupt, but when the IRQ service ++ // routine fired there is currently no detectable key. If this is ++ // true, then delay some times and re-try, but not too often. ++ ++ cols = 0; ++ for(n=0; n<10; n++) { ++ for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { ++ KEYBD_MATRIX_SET_OUTPUTS( 1 << i ); ++ udelay(KEYBD_MATRIX_SETTLING_TIME_US); ++ KEYBD_MATRIX_GET_INPUTS(keystate_cur[i]); ++ if (keystate_cur[i]) ++ cols++; ++ } ++ if (cols || rescan) ++ break; ++ udelay(KEYBD_MATRIX_SETTLING_TIME_US*50); ++ } ++ ++ // if rescan is true, we are in the process of turning on the IRQ. ++ // Ignore any spurious IRQ. However, if we got an IRQ but could not ++ // detect any key, we note this on the console, clear the keystate ++ // completely and make sure the IRQ gets re-enabled via the timer ++ // shortly. ++ ++ if (!cols && !rescan) { ++ printk("%s: spurious kbd int\n", __FUNCTION__); ++ for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { ++ keystate_cur[i] = 0; ++ keystate_prev[i] = 0; ++ } ++ mod_timer(&reenable_timer, jiffies + SCANINTERVAL); ++ return; ++ } ++ ++ pm_access(pm_keyb); ++ ++ // Okay, all went well. We now keystate_cur[] may contain the rows ++ // where we had keypresses, e.g. ++ // 0 0 0 2 0 0 0 0 0 0 0 0 0 0 ++ // We would see this if DEBUG_DUMP_KEYSTATE is on: ++ ++#ifdef DEBUG_DUMP_KEYSTATE ++ cols = 0; ++ for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { ++ printk("%d-%d ",keystate_cur[i], keystate_keep[i]); ++ } ++ printk("\n"); ++#endif ++ ++ cols = 0; ++ for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { ++ ++ // detect which key has changes doing an XOR of old state with new state ++ keystate_xor[i] = keystate_prev[i] ^ keystate_cur[i]; ++ //printk("%d: prev %d cur %d xor %d keep %d\n", i, keystate_prev[i], keystate_cur[i], keystate_xor[i], keystate_keep[i]); ++ ++ // some key changed, find out which one and do the scancode handling ++ if (keystate_xor[i] || keystate_keep[i]) { ++ for (n = 0; n < KEYBD_MATRIX_NUMBER_INPUTS; n++) ++ { ++ if ( (keystate_keep[i] & keystate_cur[i]) || ++ (keystate_xor[i] & (1 << n)) ) ++ { ++ int res; ++ code = n * KEYBD_MATRIX_NUMBER_OUTPUTS + i + 1; ++ res = kbd_dokeycode(code, keystate_cur[i] & (1 << n) ? 1 : 0); ++ kbd_setleds(); ++ if (res) { ++ keystate_keep[i] = 1 << n; ++ goto out; ++ } ++ } ++ } ++ } ++out: ++ keystate_prev[i] = keystate_cur[i]; ++ } ++ ++ ++ // fire reenable time if we are in the ISR ++ if (!rescan) ++ mod_timer(&reenable_timer, jiffies + SCANINTERVAL); ++} ++ ++ ++ ++static void kbd_reenable_timer(unsigned long dummy) ++{ ++ // re-scan the keyboard (to detect released keys) ++ kbd_scan_keyboard(1); ++ ++ // re-enable interrupts from the CPLD ++ KEYBD_MATRIX_SET_OUTPUTS( KEYBD_MATRIX_OUTPUT_MASK ); ++} ++ ++ ++ ++ ++ ++/** ++ * Referenced by request_irq() ++ */ ++static void kbd_interrupt(int irq, void *dummy, struct pt_regs *fp) ++{ ++ kbd_scan_keyboard(0); ++} ++ ++static void ramseskbd_suspend(void *data) ++{ ++ pm_suggest_suspend(); ++} ++ ++ ++static int __init ramseskbd_init(void) ++{ ++ int irq_gpio_pin; ++ ++ // Activate the normal pc-keycodes for the input-layer-keyboard ++ k_setkeycode = input_setkeycode; ++ k_getkeycode = input_getkeycode; ++ k_translate = input_translate; ++ k_unexpected_up = input_unexpected_up; ++#ifdef CONFIG_MAGIC_SYSRQ ++ k_sysrq_key = 0x54; ++ k_sysrq_xlate = input_sysrq_xlate; ++#endif ++ ++ // In linux-2.5.x we can do ++ // init_input_dev(&ramses_kbd_dev); ++ // but here we don't have this on linux-2.4, so we fill it with zeros: ++ memset(&ramses_kbd_dev, 0, sizeof(ramses_kbd_dev)); ++ ramses_kbd_dev.name = kbd_name; ++ ++ // which events we can produce (only keypresses): ++ ramses_kbd_dev.evbit[0] = BIT(EV_KEY); ++ ++ // which keypresses we can produce (all): ++ memset(&ramses_kbd_dev.keybit, 0xff, sizeof(ramses_kbd_dev.keybit)); ++ ++ // We set the 14 output columns to 0. This stops the CPLD to ++ // generate an IRQ before we finished our setup ++ KEYBD_MATRIX_SET_OUTPUTS(0); ++ ++ // Turn all LEDs off, meaning that we have the normal keymap active ++ RAMSES_LED_BLUE_OFF(); ++ RAMSES_LED_ORANGE_OFF(); ++ // TODO: used leds.c? ++ ++ // Now we make sure that the GPIO for our IRQ is programmed correctly ++ irq_gpio_pin = IRQ_TO_GPIO_2_80(RAMSES_KEYBOARD_IRQ); ++ GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin); ++ set_GPIO_IRQ_edge(irq_gpio_pin, RAMSES_KEYBOARD_IRQ_EDGE); ++ request_irq(RAMSES_KEYBOARD_IRQ, kbd_interrupt, 0, kbd_name, NULL); ++ ++ // Initialize timer to re-enable IRQs. That's our method of keyboard de-prelling ++ init_timer(&reenable_timer); ++ reenable_timer.function = kbd_reenable_timer; ++ ++ init_timer(&trigoff_timer); ++ trigoff_timer.function = kbd_stop_scanner; ++ ++ // Initialize to escape the blue mode, so we emit the current cell-phone key ++ init_timer(&cell_timer); ++ cell_timer.function = kbd_cell_timer; ++ ++ tq_suspend.routine = ramseskbd_suspend; ++ ++ // Register with Power-Management ++#ifdef PM_DEBUG ++ pm_keyb = pm_register(PM_SYS_DEV, PM_SYS_KBC+1, NULL, "ramses_keyb"); ++#else ++ pm_keyb = pm_register(PM_SYS_DEV, PM_SYS_KBC+1, NULL); ++#endif ++ ++ // Register our keyboard ++ input_register_device(&ramses_kbd_dev); ++ ++ // We set the 14 output columns to 1. This allows the CPLD to ++ // generate an IRQ when one of the rows goes high ++ KEYBD_MATRIX_SET_OUTPUTS(KEYBD_MATRIX_OUTPUT_MASK); ++ ++ return 0; ++} ++ ++ ++static void __exit ramseskbd_exit(void) ++{ ++ // make IRQs impossible, return the IRQ and unregister us ++ KEYBD_MATRIX_SET_OUTPUTS(0); ++ free_irq(RAMSES_KEYBOARD_IRQ, NULL); ++ pm_unregister(pm_keyb); ++ input_unregister_device(&ramses_kbd_dev); ++} ++ ++ ++module_init(ramseskbd_init); ++module_exit(ramseskbd_exit); ++ ++MODULE_AUTHOR("Holger Schurig "); ++MODULE_DESCRIPTION("Ramses keyboard driver"); ++MODULE_LICENSE("GPL"); ++EXPORT_SYMBOL(ramses_key); ++EXPORT_SYMBOL(ramses_kbd_dev); +--- /dev/null ++++ linux-2.4.21/drivers/input/ramses_keymap.h +@@ -0,0 +1,68 @@ ++// Normal Map ++static int ramses_keymap[][6] = { ++/* Normal Blue Orange Caps Spare Spare */ ++/* 0 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 1 */ {KEY_SUSP, KEY_SUSP, KEY_SUSP, KEY_OFF, KEY_SUSP, KEY_SUSP }, ++/* 2 */ {KEY_UP, KEY_UP, KEY_PGUP, KEY_UP, KEY_noop, KEY_noop }, ++/* 3 */ {KEY_1, KEY_SPACE, KEY_BRIM, KEY_SPACE, KEY_noop, KEY_noop }, ++/* 4 */ {KEY_4, KEY_ghi , KEY_CTRM, KEY_GHI , KEY_noop, KEY_noop }, ++/* 5 */ {KEY_7, KEY_pqrs, KEY_cel7, KEY_PQRS, KEY_noop, KEY_noop }, ++/* 6 */ {KEY_DOT, KEY_uml, KEY_celP, KEY_UML, KEY_noop, KEY_noop }, ++/* 7 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 8 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 9 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 10 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 11 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 12 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 13 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 14 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 15 */ {KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER}, ++/* 16 */ {KEY_DOWN, KEY_DOWN, KEY_PGDN, KEY_DOWN, KEY_noop, KEY_noop }, ++/* 17 */ {KEY_2, KEY_abc , KEY_BRIP, KEY_ABC , KEY_noop, KEY_noop }, ++/* 18 */ {KEY_5, KEY_jkl , KEY_CTRP, KEY_JKL, KEY_noop, KEY_noop }, ++/* 19 */ {KEY_8, KEY_tuv , KEY_cel8, KEY_TUV, KEY_noop, KEY_noop }, ++ ++/* Normal Blue Orange Caps Spare Spare */ ++/* 20 */ {KEY_0, KEY_TAB, KEY_cel0, KEY_BTAB, KEY_noop, KEY_noop }, ++/* 21 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 22 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 23 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 24 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 25 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 26 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 27 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 28 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 29 */ {KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC }, ++/* 30 */ {KEY_RIGHT, KEY_RIGHT, KEY_END, KEY_C2, KEY_noop, KEY_noop }, ++/* 31 */ {KEY_3, KEY_def, KEY_FXIT, KEY_DEF, KEY_noop, KEY_noop }, ++/* 32 */ {KEY_6, KEY_mno , KEY_FRST, KEY_MNO, KEY_noop, KEY_noop }, ++/* 33 */ {KEY_9, KEY_wxyz, KEY_cel9, KEY_WXYZ, KEY_noop, KEY_noop }, ++/* 34 */ {KEY_BACKSPACE,KEY_ATSIGN,KEY_BAR, KEY_noop, KEY_noop, KEY_noop }, ++/* 35 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 36 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 37 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 38 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 39 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++ ++/* Normal Blue Orange Caps Spare Spare */ ++/* 40 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 41 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 42 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 43 */ {KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN }, ++/* 44 */ {KEY_LEFT, KEY_LEFT, KEY_HOME, KEY_C1, KEY_noop, KEY_noop }, ++/* 45 */ {KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF }, ++/* 46 */ {KEY_F1, KEY_F4, KEY_F7, KEY_F10, KEY_noop, KEY_noop }, ++/* 47 */ {KEY_F2, KEY_F5, KEY_F8, KEY_F11, KEY_noop, KEY_noop }, ++/* 48 */ {KEY_F3, KEY_F6, KEY_F9, KEY_F12, KEY_noop, KEY_noop }, ++/* 49 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 50 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 51 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 52 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 53 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 54 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 55 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 56 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, ++/* 57 */ {KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN }, ++/* 58 */ {KEY_BLUE, KEY_NORM, KEY_CAPS, KEY_NORM, KEY_NORM, KEY_NORM }, ++/* 59 */ {KEY_ORNG, KEY_CAPS, KEY_NORM, KEY_NORM, KEY_NORM, KEY_NORM }, ++}; +--- /dev/null ++++ linux-2.4.21/drivers/input/ramses_scancodes.h +@@ -0,0 +1,134 @@ ++#ifndef _RAMSES_SCANCODES_H ++#define _RAMSES_SCANCODES_H ++ ++#define KMOD(a) (a & 0xff00) ++#undef KVAL ++#define KVAL(a) (a & 0x00ff) ++ ++// which modifiers to send before/after ++#define KM_SPECIAL 0x300 ++#define KM_CELL 0x400 ++#define KM_SHIFT 0x500 ++#define KM_ALT 0x600 ++#define KM_CTRL 0x700 ++#define KM_ALTGR 0x800 ++#define KM_ALTCTRL 0x900 ++ ++// or special keys ++#define KEY_noop KM_SPECIAL + 0 ++#define KEY_OFF KM_SPECIAL + 1 ++#define KEY_SUSP KM_SPECIAL + 2 ++#define KEY_SCAN KM_SPECIAL + 3 ++#define KEY_CTRM KM_SPECIAL + 4 ++#define KEY_CTRP KM_SPECIAL + 5 ++#define KEY_BRIM KM_SPECIAL + 6 ++#define KEY_BRIP KM_SPECIAL + 7 ++ ++#define KEY_NORM KM_SPECIAL + 10 ++#define KEY_BLUE KM_SPECIAL + 11 ++#define KEY_ORNG KM_SPECIAL + 12 ++#define KEY_CAPS KM_SPECIAL + 13 ++ ++ ++// our cell-phone like keys ++#define KEY_abc KM_CELL + 0 ++#define KEY_def KM_CELL + 1 ++#define KEY_ghi KM_CELL + 2 ++#define KEY_jkl KM_CELL + 3 ++#define KEY_mno KM_CELL + 4 ++#define KEY_pqrs KM_CELL + 5 ++#define KEY_tuv KM_CELL + 6 ++#define KEY_wxyz KM_CELL + 7 ++#define KEY_uml KM_CELL + 8 ++#define KEY_ABC KM_CELL + 9 ++#define KEY_DEF KM_CELL + 10 ++#define KEY_GHI KM_CELL + 11 ++#define KEY_JKL KM_CELL + 12 ++#define KEY_MNO KM_CELL + 13 ++#define KEY_PQRS KM_CELL + 14 ++#define KEY_TUV KM_CELL + 15 ++#define KEY_WXYZ KM_CELL + 16 ++#define KEY_UML KM_CELL + 17 ++#define KEY_cel7 KM_CELL + 18 ++#define KEY_cel8 KM_CELL + 19 ++#define KEY_cel9 KM_CELL + 20 ++#define KEY_celP KM_CELL + 21 ++#define KEY_cel0 KM_CELL + 22 ++ ++// Shift-Keys ++#define KEY_sA KM_SHIFT + KEY_A ++#define KEY_sB KM_SHIFT + KEY_B ++#define KEY_sC KM_SHIFT + KEY_C ++#define KEY_sD KM_SHIFT + KEY_D ++#define KEY_sE KM_SHIFT + KEY_E ++#define KEY_sF KM_SHIFT + KEY_F ++#define KEY_sG KM_SHIFT + KEY_G ++#define KEY_sH KM_SHIFT + KEY_H ++#define KEY_sI KM_SHIFT + KEY_I ++#define KEY_sJ KM_SHIFT + KEY_J ++#define KEY_sK KM_SHIFT + KEY_K ++#define KEY_sL KM_SHIFT + KEY_L ++#define KEY_sM KM_SHIFT + KEY_M ++#define KEY_sN KM_SHIFT + KEY_N ++#define KEY_sO KM_SHIFT + KEY_O ++#define KEY_sP KM_SHIFT + KEY_P ++#define KEY_sQ KM_SHIFT + KEY_Q ++#define KEY_sR KM_SHIFT + KEY_R ++#define KEY_sS KM_SHIFT + KEY_S ++#define KEY_sT KM_SHIFT + KEY_T ++#define KEY_sU KM_SHIFT + KEY_U ++#define KEY_sV KM_SHIFT + KEY_V ++#define KEY_sW KM_SHIFT + KEY_W ++#define KEY_sX KM_SHIFT + KEY_X ++#define KEY_sY KM_SHIFT + KEY_Y ++#define KEY_sZ KM_SHIFT + KEY_Z ++ ++// Umlaute ++#define KEY_sAE KM_SHIFT + 40 ++#define KEY_sOE KM_SHIFT + 39 ++#define KEY_sUE KM_SHIFT + 26 ++#define KEY_AE 40 ++#define KEY_OE 39 ++#define KEY_UE 26 ++#define KEY_SZ 12 ++ ++// AS400-Keys ++#define KEY_FRST KM_ALT + KEY_R ++#define KEY_FXIT KM_ALT + KEY_X ++ ++// Console-Switch ++#define KEY_C1 KM_ALTCTRL + KEY_F1 ++#define KEY_C2 KM_ALTCTRL + KEY_F2 ++ ++// additional keys from the german keyboard ++#undef KEY_MINUS ++#undef KEY_EQUAL ++#undef KEY_Y ++#undef KEY_Z ++#define KEY_Y 44 ++#define KEY_Z 21 ++#define KEY_STAR 55 ++#define KEY_COLON KM_SHIFT + 52 ++#define KEY_UNDERL KM_SHIFT + 53 ++#define KEY_ATSIGN KM_ALTGR + 16 ++#define KEY_BAR KM_ALTGR + 86 ++#define KEY_EQUAL KM_SHIFT + 11 ++#define KEY_SEMI KM_SHIFT + 51 ++#define KEY_BSLASH KM_ALTGR + 12 ++#define KEY_FSLASH KM_SHIFT + KEY_7 ++#define KEY_MINUS 53 ++#define KEY_PLUS 27 ++#define KEY_GAENSE KM_SHIFT + 3 ++#define KEY_PARA KM_SHIFT + 4 ++#define KEY_HASH 43 ++#define KEY_PGUP KEY_PAGEUP ++#define KEY_PGDN KEY_PAGEDOWN ++#define KEY_BTAB KM_SHIFT + KEY_TAB ++ ++#define MAP_NORMAL 0 ++#define MAP_BLUE 1 ++#define MAP_ORANGE 2 ++#define MAP_CAPS 3 ++#define MAX_SCANCODES 100 ++ ++#endif +--- /dev/null ++++ linux-2.4.21/drivers/input/wedge.c +@@ -0,0 +1,241 @@ ++/* ++ * Virtual keyboard wedge using input layer ++ * ++ * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH ++ * written by H.Schurig ++ * ++ * Creates a misc char device /dev/misc/wedge. Any output to this ++ * device will be translated (via a german keyboard map) into scancodes ++ * and re-submitted into the keyboard channel. Any console, X-Windows ++ * or Qt/Embedded application will be able to receive this info. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++ ++// Debug ++//#define DEBUG 1 ++#ifdef DEBUG ++# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) ++# define PRINTK(fmt, args...) printk(fmt, ## args) ++#else ++# define DPRINTK(fmt, args...) ++# define PRINTK(fmt, args...) ++#endif ++ ++ ++// Defines ++#define KBD_STUFF_MAX_BYTES 512 ++ ++// Für den IOCTL ++#define WEDGE_RAWKEY_DOWN _IOW('w', 0x72, unsigned long) ++#define WEDGE_RAWKEY_UP _IOW('w', 0x73, unsigned long) ++#define WEDGE_TS_ABS_X _IOW('w', 0x74, unsigned long) ++#define WEDGE_TS_ABS_Y _IOW('w', 0x75, unsigned long) ++#define WEDGE_TS_ABS_PRESSURE _IOW('w', 0x76, unsigned long) ++ ++// Externs ++#define MAX_NR_KEYMAPS 256 ++extern void ramses_key(int keycode); ++extern unsigned short *key_maps[MAX_NR_KEYMAPS]; ++extern struct input_dev ramses_kbd_dev; ++extern void ucb1x00_ts_evt_add(void *, u16 pressure, u16 x, u16 y); ++ ++// for special keys ++struct wedge_lookup_t { ++ u_short c; ++ u_short keysym; ++}; ++ ++struct wedge_lookup_t wedge_lookup[] = { ++ { 0x0a, 0x001c, }, ++ { 0x2f, 0x0508, }, ++}; ++ ++ ++ ++ ++static void *outbuf; ++ ++static int wedge_open(struct inode *inode, struct file *filp) ++{ ++ int ret; ++ ++ ret = -ENXIO; ++ outbuf = kmalloc(KBD_STUFF_MAX_BYTES, GFP_KERNEL); ++ if (!outbuf) ++ goto out; ++ ++ ret = 0; ++ ++out: ++ if (ret) { ++ kfree(outbuf); ++ } ++ return ret; ++} ++ ++ ++static int wedge_close(struct inode *inode, struct file *filp) ++{ ++ kfree(outbuf); ++ return 0; ++} ++ ++ ++static int wedge_search_map(u_short map[], int c) ++{ ++ int i; ++ ++ for (i=0; i"); ++MODULE_DESCRIPTION("PXA mcp low level support"); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/misc/mcp.h~ucb1x00 ++++ linux-2.4.21/drivers/misc/mcp.h +@@ -43,6 +43,7 @@ + + /* noddy implementation alert! */ + struct mcp *mcp_get(void); ++void mcp_put(void); + int mcp_register(struct mcp *); + + #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) +--- linux-2.4.21/drivers/misc/ucb1x00-core.c~pm ++++ linux-2.4.21/drivers/misc/ucb1x00-core.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -181,8 +182,9 @@ + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ +- set_current_state(TASK_INTERRUPTIBLE); +- schedule_timeout(1); ++ //HS set_current_state(TASK_INTERRUPTIBLE); ++ //HS schedule_timeout(1); ++ udelay(200); + } + + return UCB_ADC_DAT(val); +@@ -209,7 +211,8 @@ + struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data; + unsigned int isr; + +- if (rqst == PM_RESUME) { ++ switch (rqst) { ++ case PM_RESUME: + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); +@@ -521,7 +524,9 @@ + */ + static int __init ucb1x00_configure(struct ucb1x00 *ucb) + { ++#ifndef CONFIG_ARCH_RAMSES + unsigned int irq_gpio_pin = 0; ++#endif + int irq, default_irq = NO_IRQ; + + #ifdef CONFIG_ARCH_SA1100 +@@ -611,12 +616,14 @@ + /* + * Eventually, this will disappear. + */ ++#ifndef CONFIG_ARCH_RAMSES + if (irq_gpio_pin) + #ifdef CONFIG_ARCH_PXA_IDP + set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_FALLING_EDGE); + #else + set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE); + #endif ++#endif + irq = ucb1x00_detect_irq(ucb); + if (irq != NO_IRQ) { + if (default_irq != NO_IRQ && irq != default_irq) +--- linux-2.4.21/drivers/misc/ucb1x00-ts.c~ramses-ucb1x00-dejitter ++++ linux-2.4.21/drivers/misc/ucb1x00-ts.c +@@ -29,6 +29,7 @@ + + #include + #include ++#include + + #include "ucb1x00.h" + +@@ -97,7 +98,7 @@ + }; + + static struct ucb1x00_ts ucbts; +-static int adcsync = UCB_NOSYNC; ++static int adcsync = UCB_SYNC; + + static int ucb1x00_ts_startup(struct ucb1x00_ts *ts); + static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts); +@@ -116,8 +117,14 @@ + next_head = (ts->evt_head + 1) & (NR_EVENTS - 1); + if (next_head != ts->evt_tail) { + ts->events[ts->evt_head].pressure = pressure; ++#if 0 + ts->events[ts->evt_head].x = x; + ts->events[ts->evt_head].y = y; ++#else ++ // rotate by -90 ++ ts->events[ts->evt_head].x = y; ++ ts->events[ts->evt_head].y = x; ++#endif + do_gettimeofday(&ts->events[ts->evt_head].stamp); + ts->evt_head = next_head; + +@@ -256,11 +263,11 @@ + + #define ucb1x00_ts_evt_clear(ts) do { } while (0) + +-static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) ++void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) + { +- input_report_abs(&ts->idev, ABS_X, x); +- input_report_abs(&ts->idev, ABS_Y, y); +- input_report_abs(&ts->idev, ABS_PRESSURE, pressure); ++ input_report_abs(&ucbts.idev, ABS_X, y); ++ input_report_abs(&ucbts.idev, ABS_Y, x); ++ input_report_abs(&ucbts.idev, ABS_PRESSURE, pressure); + } + + static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +@@ -335,7 +342,7 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSMY, ts->adcsync); + } + + /* +@@ -346,19 +353,15 @@ + */ + static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) + { +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ unsigned int res; + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(600); + +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++ res = ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSMY, ts->adcsync); ++ return res; + } + + /* +@@ -369,19 +372,15 @@ + */ + static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) + { ++ unsigned int res; + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | +- UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +- ucb1x00_reg_write(ts->ucb, UCB_TS_CR, +- UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_TSPY_GND | UCB_TS_CR_TSMY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(300); + +- return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); ++ res = ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); ++ return res; + } + + /* +@@ -430,8 +429,9 @@ + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +-// tsk->policy = SCHED_FIFO; +-// tsk->rt_priority = 1; ++//HS ++ tsk->policy = SCHED_FIFO; ++ tsk->rt_priority = 1; + + /* only want to receive SIGKILL */ + spin_lock_irq(&tsk->sigmask_lock); +@@ -451,8 +451,8 @@ + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); +- y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); ++ y = ucb1x00_ts_read_ypos(ts); + + /* + * Switch back to interrupt mode. +@@ -461,7 +461,7 @@ + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); +- schedule_timeout(HZ / 100); ++ schedule_timeout(HZ / 200); + if (signal_pending(tsk)) + break; + +@@ -504,7 +504,7 @@ + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); +- schedule_timeout(HZ / 100); ++ schedule_timeout(HZ / 200); + } + + if (signal_pending(tsk)) +@@ -655,8 +655,8 @@ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { +- if (strcmp(p, "sync") == 0) +- adcsync = UCB_SYNC; ++ if (strcmp(p, "nosync") == 0) ++ adcsync = UCB_NOSYNC; + } + + return 1; +@@ -674,6 +674,7 @@ + module_init(ucb1x00_ts_init); + module_exit(ucb1x00_ts_exit); + ++EXPORT_SYMBOL(ucb1x00_ts_evt_add); + MODULE_AUTHOR("Russell King "); + MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); + MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/Config.in~mtd-cvs ++++ linux-2.4.21/drivers/mtd/Config.in +@@ -1,5 +1,5 @@ + +-# $Id$ ++# $Id$ + + mainmenu_option next_comment + comment 'Memory Technology Devices (MTD)' +@@ -11,10 +11,16 @@ + if [ "$CONFIG_MTD_DEBUG" = "y" ]; then + int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 + fi +- dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD ++ bool ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD + dep_tristate ' MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD + dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS +- dep_tristate ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS $CONFIG_MTD_PARTITIONS ++ if [ "$CONFIG_MTD_REDBOOT_PARTS" = "y" -o "$CONFIG_MTD_REDBOOT_PARTS" = "m" ]; then ++ bool ' Include unallocated flash space' CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ bool ' Force read-only for RedBoot system images' CONFIG_MTD_REDBOOT_PARTS_READONLY ++ fi ++ if [ "$CONFIG_MTD_PARTITIONS" = "y" ]; then ++ bool ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS ++ fi + if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS + fi +@@ -30,6 +36,7 @@ + if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then + bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW + fi ++ dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD + + source drivers/mtd/chips/Config.in + +--- linux-2.4.21/drivers/mtd/Makefile~mtd-cvs ++++ linux-2.4.21/drivers/mtd/Makefile +@@ -1,66 +1,54 @@ + # +-# Makefile for the memory technology device drivers. +-# +-# Note! Dependencies are done automagically by 'make dep', which also +-# removes any old dependencies. DON'T put your own dependencies here +-# unless it's something special (ie not a .c file). +-# +-# Note 2! The CFLAGS definitions are now inherited from the +-# parent makes.. +-# +-# $Id$ +- +- +-obj-y += chips/chipslink.o maps/mapslink.o \ +- devices/devlink.o nand/nandlink.o +-obj-m := +-obj-n := +-obj- := +- +-O_TARGET := mtdlink.o +- +-export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o +-list-multi := nftl.o +- +-mod-subdirs := +-subdir-y := chips maps devices nand +-subdir-m := $(subdir-y) +- +-# *** BIG UGLY NOTE *** +-# +-# The shiny new inter_module_xxx has introduced yet another ugly link +-# order dependency, which I'd previously taken great care to avoid. +-# We now have to ensure that the chip drivers are initialised before the +-# map drivers, and that the doc200[01] drivers are initialised before +-# docprobe. +-# +-# We'll hopefully merge the doc200[01] drivers and docprobe back into +-# a single driver some time soon, but the CFI drivers are going to have +-# to stay like that. +-# +-# Urgh. ++# linux/drivers/Makefile.24 ++# Makefile for obsolete kernels. + # +-# dwmw2 21/11/0 ++# $Id$ + + # Core functionality. +-obj-$(CONFIG_MTD) += mtdcore.o ++mtd-y := mtdcore.o ++mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o ++obj-$(CONFIG_MTD) += $(mtd-y) ++ + obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o +-obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o +-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +-obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o +-obj-$(CONFIG_FTL) += ftl.o +-obj-$(CONFIG_NFTL) += nftl.o ++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs-24.o ++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs-24.o ++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs-24.o ++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs-24.o ++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs-24.o + + nftl-objs := nftlcore.o nftlmount.o ++inftl-objs := inftlcore.o inftlmount.o ++ ++export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \ ++ mtdconcat.o mtd_blkdevs-24.o ++ ++mtd_blkdevs-objs := mtd_blkdevs-24.o ++ ++obj-y += chips/chipslink.o maps/mapslink.o \ ++ devices/devlink.o nand/nandlink.o ++ ++O_TARGET := mtdlink.o ++ ++list-multi := nftl.o inftl.o mtd_blkdevs-24.o ++ ++mod-subdirs := ++subdir-y := chips maps devices nand ++subdir-m := $(subdir-y) + + include $(TOPDIR)/Rules.make + + nftl.o: $(nftl-objs) + $(LD) -r -o $@ $(nftl-objs) + ++inftl.o: $(inftl-objs) ++ $(LD) -r -o $@ $(inftl-objs) ++ ++mtd_blkdevs.o: $(mtd_blkdevs-objs) ++ $(LD) -r -o $@ $(mtd_blkdevs-objs) ++ +--- /dev/null ++++ linux-2.4.21/drivers/mtd/Makefile.common +@@ -0,0 +1,27 @@ ++# ++# Makefile for the memory technology device drivers. ++# ++# $Id$ ++ ++# Core functionality. ++mtd-y := mtdcore.o ++mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o ++obj-$(CONFIG_MTD) += $(mtd-y) ++ ++obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o ++obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o ++obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_AFS_PARTS) += afs.o ++ ++# 'Users' - code which presents functionality to userspace. ++obj-$(CONFIG_MTD_CHAR) += mtdchar.o ++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o ++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o ++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o ++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o ++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o ++ ++nftl-objs := nftlcore.o nftlmount.o ++inftl-objs := inftlcore.o inftlmount.o ++ ++obj-y += chips/ maps/ devices/ nand/ +--- linux-2.4.21/drivers/mtd/afs.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/afs.c +@@ -21,7 +21,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id$ ++ $Id$ + + ======================================================================*/ + +@@ -57,6 +57,17 @@ + u32 checksum; /* Image checksum (inc. this struct) */ + }; + ++static u32 word_sum(void *words, int num) ++{ ++ u32 *p = words; ++ u32 sum = 0; ++ ++ while (num--) ++ sum += *p++; ++ ++ return sum; ++} ++ + static int + afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start, + u_int off, u_int mask) +@@ -76,17 +87,25 @@ + return ret; + } + ++ ret = 1; ++ + /* + * Does it contain the magic number? + */ + if (fs.signature != 0xa0ffff9f) +- ret = 1; ++ ret = 0; ++ ++ /* ++ * Check the checksum. ++ */ ++ if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff) ++ ret = 0; + + /* + * Don't touch the SIB. + */ + if (fs.type == 2) +- ret = 1; ++ ret = 0; + + *iis_start = fs.image_info_base & mask; + *img_start = fs.image_start & mask; +@@ -96,14 +115,14 @@ + * be located after the footer structure. + */ + if (*iis_start >= ptr) +- ret = 1; ++ ret = 0; + + /* + * Check the start of this image. The image + * data can not be located after this block. + */ + if (*img_start > off) +- ret = 1; ++ ret = 0; + + return ret; + } +@@ -112,20 +131,41 @@ + afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr) + { + size_t sz; +- int ret; ++ int ret, i; + + memset(iis, 0, sizeof(*iis)); + ret = mtd->read(mtd, ptr, sizeof(*iis), &sz, (u_char *) iis); +- if (ret >= 0 && sz != sizeof(*iis)) +- ret = -EINVAL; + if (ret < 0) ++ goto failed; ++ ++ if (sz != sizeof(*iis)) { ++ ret = -EINVAL; ++ goto failed; ++ } ++ ++ ret = 0; ++ ++ /* ++ * Validate the name - it must be NUL terminated. ++ */ ++ for (i = 0; i < sizeof(iis->name); i++) ++ if (iis->name[i] == '\0') ++ break; ++ ++ if (i < sizeof(iis->name)) ++ ret = 1; ++ ++ return ret; ++ ++ failed: + printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", + ptr, ret); +- + return ret; + } + +-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts) ++static int parse_afs_partitions(struct mtd_info *mtd, ++ struct mtd_partition **pparts, ++ unsigned long origin) + { + struct mtd_partition *parts; + u_int mask, off, idx, sz; +@@ -150,12 +190,14 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + ret = afs_read_iis(mtd, &iis, iis_ptr); + if (ret < 0) + break; ++ if (ret == 0) ++ continue; + + sz += sizeof(struct mtd_partition); + sz += strlen(iis.name) + 1; +@@ -183,13 +225,15 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + /* Read the image info block */ + ret = afs_read_iis(mtd, &iis, iis_ptr); + if (ret < 0) + break; ++ if (ret == 0) ++ continue; + + strcpy(str, iis.name); + size = mtd->erasesize + off - img_ptr; +@@ -227,7 +271,25 @@ + return idx ? idx : ret; + } + +-EXPORT_SYMBOL(parse_afs_partitions); ++static struct mtd_part_parser afs_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_afs_partitions, ++ .name = "afs", ++}; ++ ++static int __init afs_parser_init(void) ++{ ++ return register_mtd_parser(&afs_parser); ++} ++ ++static void __exit afs_parser_exit(void) ++{ ++ deregister_mtd_parser(&afs_parser); ++} ++ ++module_init(afs_parser_init); ++module_exit(afs_parser_exit); ++ + + MODULE_AUTHOR("ARM Ltd"); + MODULE_DESCRIPTION("ARM Firmware Suite partition parser"); +--- linux-2.4.21/drivers/mtd/chips/Config.in~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/Config.in +@@ -1,6 +1,6 @@ + # drivers/mtd/chips/Config.in + +-# $Id$ ++# $Id$ + + mainmenu_option next_comment + +@@ -18,6 +18,7 @@ + define_bool CONFIG_MTD_GEN_PROBE n + fi + fi ++ + if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then + bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS + if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then +@@ -27,11 +28,13 @@ + LITTLE_ENDIAN_BYTE CONFIG_MTD_CFI_LE_BYTE_SWAP" NO + bool ' Specific CFI Flash geometry selection' CONFIG_MTD_CFI_GEOMETRY + if [ "$CONFIG_MTD_CFI_GEOMETRY" = "y" ]; then +- bool ' Support 8-bit buswidth' CONFIG_MTD_CFI_B1 +- bool ' Support 16-bit buswidth' CONFIG_MTD_CFI_B2 +- bool ' Support 32-bit buswidth' CONFIG_MTD_CFI_B4 +- bool ' Support 64-bit buswidth' CONFIG_MTD_CFI_B8 +- if [ "$CONFIG_MTD_CFI_B1" = "y" ]; then ++ bool ' Support 8-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_1 ++ bool ' Support 16-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_2 ++ bool ' Support 32-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_4 ++ bool ' Support 64-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_8 ++ bool ' Support 128-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_16 ++ bool ' Support 256-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_32 ++ if [ "$CONFIG_MTD_MAP_BANK_WIDTH_1" = "y" ]; then + define_bool CONFIG_MTD_CFI_I1 y + else + bool ' Support 1-chip flash interleave' CONFIG_MTD_CFI_I1 +@@ -46,6 +49,20 @@ + dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE + dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE + ++if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL y ++else ++ if [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL m ++ else ++ define_bool CONFIG_MTD_CFI_UTIL n ++ fi ++fi ++ + dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD + dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD + dep_tristate ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD +--- linux-2.4.21/drivers/mtd/chips/Makefile~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/Makefile +@@ -1,31 +1,12 @@ + # +-# linux/drivers/chips/Makefile ++# linux/drivers/chips/Makefile.24 ++# Makefile for obsolete kernels. + # +-# $Id$ ++# $Id$ + + O_TARGET := chipslink.o ++export-objs := chipreg.o gen_probe.o cfi_util.o + +-export-objs := chipreg.o gen_probe.o +- +-# *** BIG UGLY NOTE *** +-# +-# The removal of get_module_symbol() and replacement with +-# inter_module_register() et al has introduced a link order dependency +-# here where previously there was none. We now have to ensure that +-# the CFI command set drivers are linked before cfi_probe.o +- +-obj-$(CONFIG_MTD) += chipreg.o +-obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o +-obj-$(CONFIG_MTD_CFI) += cfi_probe.o +-obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o +-obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o +-obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o +-obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o +-obj-$(CONFIG_MTD_JEDEC) += jedec.o +-obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o +-obj-$(CONFIG_MTD_RAM) += map_ram.o +-obj-$(CONFIG_MTD_ROM) += map_rom.o +-obj-$(CONFIG_MTD_SHARP) += sharp.o +-obj-$(CONFIG_MTD_ABSENT) += map_absent.o ++include Makefile.common + + include $(TOPDIR)/Rules.make +--- /dev/null ++++ linux-2.4.21/drivers/mtd/chips/Makefile.common +@@ -0,0 +1,26 @@ ++# ++# linux/drivers/chips/Makefile ++# ++# $Id$ ++ ++# *** BIG UGLY NOTE *** ++# ++# The removal of get_module_symbol() and replacement with ++# inter_module_register() et al has introduced a link order dependency ++# here where previously there was none. We now have to ensure that ++# the CFI command set drivers are linked before gen_probe.o ++ ++obj-$(CONFIG_MTD) += chipreg.o ++obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o ++obj-$(CONFIG_MTD_CFI) += cfi_probe.o ++obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o ++obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o ++obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o ++obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o ++obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o ++obj-$(CONFIG_MTD_JEDEC) += jedec.o ++obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o ++obj-$(CONFIG_MTD_RAM) += map_ram.o ++obj-$(CONFIG_MTD_ROM) += map_rom.o ++obj-$(CONFIG_MTD_SHARP) += sharp.o ++obj-$(CONFIG_MTD_ABSENT) += map_absent.o +--- linux-2.4.21/drivers/mtd/chips/amd_flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/amd_flash.c +@@ -3,7 +3,7 @@ + * + * Author: Jonas Holmberg + * +- * $Id$ ++ * $Id$ + * + * Copyright (c) 2001 Axis Communications AB + * +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -66,7 +67,6 @@ + #define AM29LV160DT 0x22C4 + #define AM29LV160DB 0x2249 + #define AM29BDS323D 0x22D1 +-#define AM29BDS643D 0x227E + + /* Atmel */ + #define AT49xV16x 0x00C0 +@@ -125,10 +125,10 @@ + + + static struct mtd_chip_driver amd_flash_chipdrv = { +- probe: amd_flash_probe, +- destroy: amd_flash_destroy, +- name: "amd_flash", +- module: THIS_MODULE ++ .probe = amd_flash_probe, ++ .destroy = amd_flash_destroy, ++ .name = "amd_flash", ++ .module = THIS_MODULE + }; + + +@@ -140,11 +140,11 @@ + static inline __u32 wide_read(struct map_info *map, __u32 addr) + { + if (map->buswidth == 1) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (map->buswidth == 2) { +- return map->read16(map, addr); ++ return map_read16(map, addr); + } else if (map->buswidth == 4) { +- return map->read32(map, addr); ++ return map_read32(map, addr); + } + + return 0; +@@ -153,11 +153,11 @@ + static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) + { + if (map->buswidth == 1) { +- map->write8(map, val, addr); ++ map_write8(map, val, addr); + } else if (map->buswidth == 2) { +- map->write16(map, val, addr); ++ map_write16(map, val, addr); + } else if (map->buswidth == 4) { +- map->write32(map, val, addr); ++ map_write32(map, val, addr); + } + } + +@@ -424,231 +424,217 @@ + + static struct mtd_info *amd_flash_probe(struct map_info *map) + { +- /* Keep this table on the stack so that it gets deallocated after the +- * probe is done. +- */ +- const struct amd_flash_info table[] = { ++ static const struct amd_flash_info table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BB, +- name: "Fujitsu MBM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BB, ++ .name = "Fujitsu MBM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W800T, ++ .name = "ST M29W800T", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS323D, +- name: "AMD AM29BDS323D", +- size: 0x00400000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 48 }, +- { offset: 0x300000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS643D, +- name: "AMD AM29BDS643D", +- size: 0x00800000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 96 }, +- { offset: 0x600000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29BDS323D, ++ .name = "AMD AM29BDS323D", ++ .size = 0x00400000, ++ .numeraseregions = 3, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, ++ { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16x, +- name: "Atmel AT49xV16x", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x02000, numblocks: 8 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16x, ++ .name = "Atmel AT49xV16x", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16xT, +- name: "Atmel AT49xV16xT", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16xT, ++ .name = "Atmel AT49xV16xT", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } + } + } + }; +@@ -720,7 +706,7 @@ + "memory for MTD erase region info\n", map->name); + kfree(mtd); + map->fldrv_priv = NULL; +- return 0; ++ return NULL; + } + + reg_idx = 0; +@@ -782,8 +768,8 @@ + map->fldrv_priv = private; + + map->fldrv = &amd_flash_chipdrv; +- MOD_INC_USE_COUNT; + ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -822,7 +808,7 @@ + + chip->state = FL_READY; + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +@@ -984,7 +970,7 @@ + u_char tmp_buf[4]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + bus_ofs + private->chips[chipnum].start, + map->buswidth); + while (len && i < map->buswidth) +@@ -1057,7 +1043,7 @@ + u_char tmp_buf[2]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + ofs + private->chips[chipnum].start, + map->buswidth); + while (len--) { +@@ -1124,7 +1110,7 @@ + timeo = jiffies + (HZ * 20); + + spin_unlock_bh(chip->mutex); +- schedule_timeout(HZ); ++ msleep(1000); + spin_lock_bh(chip->mutex); + + while (flash_is_busy(map, adr, private->interleave)) { +@@ -1178,7 +1164,7 @@ + __u8 verify; + + for (address = adr; address < (adr + size); address++) { +- if ((verify = map->read8(map, address)) != 0xFF) { ++ if ((verify = map_read8(map, address)) != 0xFF) { + error = 1; + break; + } +@@ -1309,9 +1295,7 @@ + } + + instr->state = MTD_ERASE_DONE; +- if (instr->callback) { +- instr->callback(instr); +- } ++ mtd_erase_callback(instr); + + return 0; + } +--- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0001.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0001.c +@@ -4,7 +4,7 @@ + * + * (C) 2000 Red Hat. GPL'd + * +- * $Id$ ++ * $Id$ + * + * + * 10/10/2000 Nicolas Pitre +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -28,21 +29,39 @@ + #include + #include + #include ++#include + #include +-#include ++#include + #include ++#include + +-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE ++/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ ++/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ ++ ++// debugging, turns off buffer write mode if set to 1 ++#define FORCE_WORD_WRITE 0 ++ ++#define MANUFACTURER_INTEL 0x0089 ++#define I82802AB 0x00ad ++#define I82802AC 0x00ac ++#define MANUFACTURER_ST 0x0020 ++#define M50LPW080 0x002F + + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); + static void cfi_intelext_sync (struct mtd_info *); + static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); + static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); ++static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); ++static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); ++static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); ++static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); ++static int cfi_intelext_get_fact_prot_info (struct mtd_info *, ++ struct otp_info *, size_t); ++static int cfi_intelext_get_user_prot_info (struct mtd_info *, ++ struct otp_info *, size_t); + static int cfi_intelext_suspend (struct mtd_info *); + static void cfi_intelext_resume (struct mtd_info *); + +@@ -50,18 +69,29 @@ + + struct mtd_info *cfi_cmdset_0001(struct map_info *, int); + +-static struct mtd_info *cfi_intelext_setup (struct map_info *); ++static struct mtd_info *cfi_intelext_setup (struct mtd_info *); ++static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **); + +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf); +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len); + ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); ++#include "fwh_lock.h" ++ ++ ++ ++/* ++ * *********** SETUP AND PROBE BITS *********** ++ */ ++ + static struct mtd_chip_driver cfi_intelext_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_intelext_destroy, +- name: "cfi_cmdset_0001", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_intelext_destroy, ++ .name = "cfi_cmdset_0001", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -81,7 +111,8 @@ + printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); + printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); + printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); +- for (i=9; i<32; i++) { ++ printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported"); ++ for (i=10; i<32; i++) { + if (extp->FeatureSupport & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); ++ extp->VccOptimal >> 4, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", +- extp->VppOptimal >> 8, extp->VppOptimal & 0xf); ++ extp->VppOptimal >> 4, extp->VppOptimal & 0xf); + } + #endif + ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ ++static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ ++ printk(KERN_WARNING "cfi_cmdset_0001: Suspend " ++ "erase on write disabled.\n"); ++ extp->SuspendCmdSupport &= ~1; ++} ++#endif ++ ++#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND ++static void fixup_no_write_suspend(struct mtd_info *mtd, void* param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_intelext *cfip = cfi->cmdset_priv; ++ ++ if (cfip && (cfip->FeatureSupport&4)) { ++ cfip->FeatureSupport &= ~4; ++ printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); ++ } ++} ++#endif ++ ++static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ ++ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ ++} ++ ++static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ /* Note this is done after the region info is endian swapped */ ++ cfi->cfiq->EraseRegionInfo[1] = ++ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; ++}; ++ ++static void fixup_use_point(struct mtd_info *mtd, void *param) ++{ ++ struct map_info *map = mtd->priv; ++ if (!mtd->point && map_is_linear(map)) { ++ mtd->point = cfi_intelext_point; ++ mtd->unpoint = cfi_intelext_unpoint; ++ } ++} ++ ++static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ if (cfi->cfiq->BufWriteTimeoutTyp) { ++ printk(KERN_INFO "Using buffer write method\n" ); ++ mtd->write = cfi_intelext_write_buffers; ++ } ++} ++ ++static struct cfi_fixup cfi_fixup_table[] = { ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, ++#endif ++#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL }, ++#endif ++#if !FORCE_WORD_WRITE ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, ++#endif ++ { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, ++ { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, ++ { 0, 0, NULL, NULL } ++}; ++ ++static struct cfi_fixup jedec_fixup_table[] = { ++ { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, ++ { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, ++ { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, ++ { 0, 0, NULL, NULL } ++}; ++static struct cfi_fixup fixup_table[] = { ++ /* The CFI vendor ids and the JEDEC vendor IDs appear ++ * to be common. It is like the devices id's are as ++ * well. This table is to pick all cases where ++ * we know that is the case. ++ */ ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL }, ++ { 0, 0, NULL, NULL } ++}; ++ ++static inline struct cfi_pri_intelext * ++read_pri_intelext(struct map_info *map, __u16 adr) ++{ ++ struct cfi_pri_intelext *extp; ++ unsigned int extp_size = sizeof(*extp); ++ ++ again: ++ extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp"); ++ if (!extp) ++ return NULL; ++ ++ /* Do some byteswapping if necessary */ ++ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); ++ extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); ++ extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); ++ ++ if (extp->MajorVersion == '1' && extp->MinorVersion == '3') { ++ unsigned int extra_size = 0; ++ int nb_parts, i; ++ ++ /* Protection Register info */ ++ extra_size += (extp->NumProtectionFields - 1) * ++ sizeof(struct cfi_intelext_otpinfo); ++ ++ /* Burst Read info */ ++ extra_size += 6; ++ ++ /* Number of hardware-partitions */ ++ extra_size += 1; ++ if (extp_size < sizeof(*extp) + extra_size) ++ goto need_more; ++ nb_parts = extp->extra[extra_size - 1]; ++ ++ for (i = 0; i < nb_parts; i++) { ++ struct cfi_intelext_regioninfo *rinfo; ++ rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size]; ++ extra_size += sizeof(*rinfo); ++ if (extp_size < sizeof(*extp) + extra_size) ++ goto need_more; ++ rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions); ++ extra_size += (rinfo->NumBlockTypes - 1) ++ * sizeof(struct cfi_intelext_blockinfo); ++ } ++ ++ if (extp_size < sizeof(*extp) + extra_size) { ++ need_more: ++ extp_size = sizeof(*extp) + extra_size; ++ kfree(extp); ++ if (extp_size > 4096) { ++ printk(KERN_ERR ++ "%s: cfi_pri_intelext is too fat\n", ++ __FUNCTION__); ++ return NULL; ++ } ++ goto again; ++ } ++ } ++ ++ return extp; ++} ++ + /* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The +@@ -119,8 +308,29 @@ + struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) + { + struct cfi_private *cfi = map->fldrv_priv; ++ struct mtd_info *mtd; + int i; +- __u32 base = cfi->chips[0].start; ++ ++ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); ++ if (!mtd) { ++ printk(KERN_ERR "Failed to allocate memory for MTD device\n"); ++ return NULL; ++ } ++ memset(mtd, 0, sizeof(*mtd)); ++ mtd->priv = map; ++ mtd->type = MTD_NORFLASH; ++ ++ /* Fill in the default mtd operations */ ++ mtd->erase = cfi_intelext_erase_varsize; ++ mtd->read = cfi_intelext_read; ++ mtd->write = cfi_intelext_write_words; ++ mtd->sync = cfi_intelext_sync; ++ mtd->lock = cfi_intelext_lock; ++ mtd->unlock = cfi_intelext_unlock; ++ mtd->suspend = cfi_intelext_suspend; ++ mtd->resume = cfi_intelext_resume; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->name = map->name; + + if (cfi->cfi_mode == CFI_MODE_CFI) { + /* +@@ -130,40 +340,17 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; + +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); ++ extp = read_pri_intelext(map, adr); + if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); ++ kfree(mtd); + return NULL; + } + +- /* Read in the Extended Query Table */ +- for (i=0; iMajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { +- printk(KERN_WARNING " Unknown IntelExt Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); +- return NULL; +- } ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; + +- /* Do some byteswapping if necessary */ +- extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); +- extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); +- extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); ++ cfi_fixup(mtd, cfi_fixup_table); + + #ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ +@@ -171,19 +358,15 @@ + #endif + + if(extp->SuspendCmdSupport & 1) { +-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ +- printk(KERN_WARNING "cfi_cmdset_0001: Suspend " +- "erase on write disabled.\n"); +- extp->SuspendCmdSupport &= ~1; +-#else + printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); +-#endif + } +- /* Install our own private info structure */ +- cfi->cmdset_priv = extp; + } ++ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { ++ /* Apply jedec specific fixups */ ++ cfi_fixup(mtd, jedec_fixup_table); ++ } ++ /* Apply generic fixups */ ++ cfi_fixup(mtd, fixup_table); + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; +@@ -194,30 +377,19 @@ + + map->fldrv = &cfi_intelext_chipdrv; + +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); +- return cfi_intelext_setup(map); ++ return cfi_intelext_setup(mtd); + } + +-static struct mtd_info *cfi_intelext_setup(struct map_info *map) ++static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) + { ++ struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- struct mtd_info *mtd; + unsigned long offset = 0; + int i,j; + unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; + +- mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); + +- if (!mtd) { +- printk(KERN_ERR "Failed to allocate memory for MTD device\n"); +- goto setup_err; +- } +- +- memset(mtd, 0, sizeof(*mtd)); +- mtd->priv = map; +- mtd->type = MTD_NORFLASH; + mtd->size = devsize * cfi->numchips; + + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; +@@ -257,37 +429,21 @@ + mtd->eraseregions[i].numblocks); + } + +- /* Also select the correct geometry setup too */ +- mtd->erase = cfi_intelext_erase_varsize; +- mtd->read = cfi_intelext_read; ++#ifdef CONFIG_MTD_OTP ++ mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; ++ mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; ++ mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg; ++ mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; ++ mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info; ++ mtd->get_user_prot_info = cfi_intelext_get_user_prot_info; ++#endif + +- if(map->point && map->unpoint){ +- mtd->point = do_point; +- mtd->unpoint = do_unpoint; +- } ++ /* This function has the potential to distort the reality ++ a bit and therefore should be called last. */ ++ if (cfi_intelext_partition_fixup(mtd, &cfi) != 0) ++ goto setup_err; + +-#ifndef FORCE_WORD_WRITE +- if ( cfi->cfiq->BufWriteTimeoutTyp ) { +- printk("Using buffer write method\n" ); +- mtd->write = cfi_intelext_write_buffers; +- } else { +-#else +- { +-#endif +- printk("Using word write method\n" ); +- mtd->write = cfi_intelext_write_words; +- } +- mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; +- mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; +- mtd->sync = cfi_intelext_sync; +- mtd->lock = cfi_intelext_lock; +- mtd->unlock = cfi_intelext_unlock; +- mtd->suspend = cfi_intelext_suspend; +- mtd->resume = cfi_intelext_resume; +- mtd->flags = MTD_CAP_NORFLASH; +- map->fldrv = &cfi_intelext_chipdrv; +- MOD_INC_USE_COUNT; +- mtd->name = map->name; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -297,82 +453,584 @@ + kfree(mtd); + } + kfree(cfi->cmdset_priv); +- kfree(cfi->cfiq); + return NULL; + } + +-static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++static int cfi_intelext_partition_fixup(struct mtd_info *mtd, ++ struct cfi_private **pcfi) + { +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- unsigned long cmd_addr; +- struct cfi_private *cfi = map->fldrv_priv; ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = *pcfi; ++ struct cfi_pri_intelext *extp = cfi->cmdset_priv; + +- adr += chip->start; ++ /* ++ * Probing of multi-partition flash ships. ++ * ++ * To support multiple partitions when available, we simply arrange ++ * for each of them to have their own flchip structure even if they ++ * are on the same physical chip. This means completely recreating ++ * a new cfi_private structure right here which is a blatent code ++ * layering violation, but this is still the least intrusive ++ * arrangement at this point. This can be rearranged in the future ++ * if someone feels motivated enough. --nico ++ */ ++ if (extp && extp->MajorVersion == '1' && extp->MinorVersion == '3' ++ && extp->FeatureSupport & (1 << 9)) { ++ struct cfi_private *newcfi; ++ struct flchip *chip; ++ struct flchip_shared *shared; ++ int offs, numregions, numparts, partshift, numvirtchips, i, j; + +- /* Ensure cmd read/writes are aligned. */ +- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ /* Protection Register info */ ++ offs = (extp->NumProtectionFields - 1) * ++ sizeof(struct cfi_intelext_otpinfo); + +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); ++ /* Burst Read info */ ++ offs += 6; + ++ /* Number of partition regions */ ++ numregions = extp->extra[offs]; ++ offs += 1; ++ ++ /* Number of hardware partitions */ ++ numparts = 0; ++ for (i = 0; i < numregions; i++) { ++ struct cfi_intelext_regioninfo *rinfo; ++ rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs]; ++ numparts += rinfo->NumIdentPartitions; ++ offs += sizeof(*rinfo) ++ + (rinfo->NumBlockTypes - 1) * ++ sizeof(struct cfi_intelext_blockinfo); ++ } ++ ++ /* ++ * All functions below currently rely on all chips having ++ * the same geometry so we'll just assume that all hardware ++ * partitions are of the same size too. ++ */ ++ partshift = cfi->chipshift - __ffs(numparts); ++ ++ if ((1 << partshift) < mtd->erasesize) { ++ printk( KERN_ERR ++ "%s: bad number of hw partitions (%d)\n", ++ __FUNCTION__, numparts); ++ return -EINVAL; ++ } ++ ++ numvirtchips = cfi->numchips * numparts; ++ newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); ++ if (!newcfi) ++ return -ENOMEM; ++ shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL); ++ if (!shared) { ++ kfree(newcfi); ++ return -ENOMEM; ++ } ++ memcpy(newcfi, cfi, sizeof(struct cfi_private)); ++ newcfi->numchips = numvirtchips; ++ newcfi->chipshift = partshift; ++ ++ chip = &newcfi->chips[0]; ++ for (i = 0; i < cfi->numchips; i++) { ++ shared[i].writing = shared[i].erasing = NULL; ++ spin_lock_init(&shared[i].lock); ++ for (j = 0; j < numparts; j++) { ++ *chip = cfi->chips[i]; ++ chip->start += j << partshift; ++ chip->priv = &shared[i]; ++ /* those should be reset too since ++ they create memory references. */ ++ init_waitqueue_head(&chip->wq); ++ spin_lock_init(&chip->_spinlock); ++ chip->mutex = &chip->_spinlock; ++ chip++; ++ } ++ } ++ ++ printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips " ++ "--> %d partitions of %d KiB\n", ++ map->name, cfi->numchips, cfi->interleave, ++ newcfi->numchips, 1<<(newcfi->chipshift-10)); ++ ++ map->fldrv_priv = newcfi; ++ *pcfi = newcfi; ++ kfree(cfi); ++ } ++ ++ return 0; ++} ++ ++/* ++ * *********** CHIP ACCESS FUNCTIONS *********** ++ */ ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct cfi_private *cfi = map->fldrv_priv; ++ map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); ++ unsigned long timeo; ++ struct cfi_pri_intelext *cfip = cfi->cmdset_priv; ++ ++ resettime: + timeo = jiffies + HZ; + retry: ++ if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { ++ /* ++ * OK. We have possibility for contension on the write/erase ++ * operations which are global to the real chip and not per ++ * partition. So let's fight it over in the partition which ++ * currently has authority on the operation. ++ * ++ * The rules are as follows: ++ * ++ * - any write operation must own shared->writing. ++ * ++ * - any erase operation must own _both_ shared->writing and ++ * shared->erasing. ++ * ++ * - contension arbitration is handled in the owner's context. ++ * ++ * The 'shared' struct can be read when its lock is taken. ++ * However any writes to it can only be made when the current ++ * owner's lock is also held. ++ */ ++ struct flchip_shared *shared = chip->priv; ++ struct flchip *contender; ++ spin_lock(&shared->lock); ++ contender = shared->writing; ++ if (contender && contender != chip) { ++ /* ++ * The engine to perform desired operation on this ++ * partition is already in use by someone else. ++ * Let's fight over it in the context of the chip ++ * currently using it. If it is possible to suspend, ++ * that other partition will do just that, otherwise ++ * it'll happily send us to sleep. In any case, when ++ * get_chip returns success we're clear to go ahead. ++ */ ++ int ret = spin_trylock(contender->mutex); ++ spin_unlock(&shared->lock); ++ if (!ret) ++ goto retry; ++ spin_unlock(chip->mutex); ++ ret = get_chip(map, contender, contender->start, mode); + spin_lock(chip->mutex); ++ if (ret) { ++ spin_unlock(contender->mutex); ++ return ret; ++ } ++ timeo = jiffies + HZ; ++ spin_lock(&shared->lock); ++ } ++ ++ /* We now own it */ ++ shared->writing = chip; ++ if (mode == FL_ERASING) ++ shared->erasing = chip; ++ if (contender && contender != chip) ++ spin_unlock(contender->mutex); ++ spin_unlock(&shared->lock); ++ } + +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ + switch (chip->state) { + +- case FL_READY: +- case FL_POINT: ++ case FL_STATUS: ++ for (;;) { ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + ++ /* At this point we're fine with write operations ++ in other partitions as they don't conflict. */ ++ if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %lx\n", ++ status.x[0]); ++ return -EIO; ++ } ++ spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } ++ ++ case FL_READY: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; ++ return 0; + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; ++ case FL_ERASING: ++ if (!cfip || ++ !(cfip->FeatureSupport & 2) || ++ !(mode == FL_READY || mode == FL_POINT || ++ (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) ++ goto sleep; ++ ++ ++ /* Erase suspend */ ++ map_write(map, CMD(0xB0), adr); ++ ++ /* If the flash has finished erasing, then 'erase suspend' ++ * appears to make some (28F320) flash devices switch to ++ * 'read' mode. Make sure that we switch to 'read status' ++ * mode so we get the right data. --rmk ++ */ ++ map_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; +- } + +- /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); ++ /* Urgh. Resume and pretend we weren't here. */ ++ map_write(map, CMD(0xd0), adr); ++ /* Make sure we're in 'read status' mode if it had finished */ ++ map_write(map, CMD(0x70), adr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "Chip not ready after erase " ++ "suspended: status = 0x%lx\n", status.x[0]); + return -EIO; + } + +- /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); + cfi_udelay(1); +- goto retry; ++ spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_STATUS; ++ return 0; ++ ++ case FL_XIP_WHILE_ERASING: ++ if (mode != FL_READY && mode != FL_POINT && ++ (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) ++ goto sleep; ++ chip->oldstate = chip->state; ++ chip->state = FL_READY; ++ return 0; ++ ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; + + default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ ++ sleep: + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ spin_lock(chip->mutex); ++ goto resettime; ++ } ++} ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ if (chip->priv) { ++ struct flchip_shared *shared = chip->priv; ++ spin_lock(&shared->lock); ++ if (shared->writing == chip && chip->oldstate == FL_READY) { ++ /* We own the ability to write, but we're done */ ++ shared->writing = shared->erasing; ++ if (shared->writing && shared->writing != chip) { ++ /* give back ownership to who we loaned it from */ ++ struct flchip *loaner = shared->writing; ++ spin_lock(loaner->mutex); ++ spin_unlock(&shared->lock); ++ spin_unlock(chip->mutex); ++ put_chip(map, loaner, loaner->start); ++ spin_lock(chip->mutex); ++ spin_unlock(loaner->mutex); ++ wake_up(&chip->wq); ++ return; ++ } ++ shared->erasing = NULL; ++ shared->writing = NULL; ++ } else if (shared->erasing == chip && shared->writing != chip) { ++ /* ++ * We own the ability to erase without the ability ++ * to write, which means the erase was suspended ++ * and some other partition is currently writing. ++ * Don't let the switch below mess things up since ++ * we don't have ownership to resume anything. ++ */ ++ spin_unlock(&shared->lock); ++ wake_up(&chip->wq); ++ return; ++ } ++ spin_unlock(&shared->lock); ++ } ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ /* What if one interleaved chip has finished and the ++ other hasn't? The old code would leave the finished ++ one in READY mode. That's bad, and caused -EROFS ++ errors to be returned from do_erase_oneblock because ++ that's the only bit it checked for at the time. ++ As the state machine appears to explicitly allow ++ sending the 0x70 (Read Status) command to an erasing ++ chip and expecting it to be ignored, that's what we ++ do. */ ++ map_write(map, CMD(0xd0), adr); ++ map_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_XIP_WHILE_ERASING: ++ chip->state = chip->oldstate; ++ chip->oldstate = FL_READY; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ case FL_JEDEC_QUERY: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate); ++ } ++ wake_up(&chip->wq); ++} ++ ++#ifdef CONFIG_MTD_XIP ++ ++/* ++ * No interrupt what so ever can be serviced while the flash isn't in array ++ * mode. This is ensured by the xip_disable() and xip_enable() functions ++ * enclosing any code path where the flash is known not to be in array mode. ++ * And within a XIP disabled code path, only functions marked with __xipram ++ * may be called and nothing else (it's a good thing to inspect generated ++ * assembly to make sure inline functions were actually inlined and that gcc ++ * didn't emit calls to its own support functions). Also configuring MTD CFI ++ * support to a single buswidth and a single interleave is also recommended. ++ * Note that not only IRQs are disabled but the preemption count is also ++ * increased to prevent other locking primitives (namely spin_unlock) from ++ * decrementing the preempt count to zero and scheduling the CPU away while ++ * not in array mode. ++ */ ++ ++static void xip_disable(struct map_info *map, struct flchip *chip, ++ unsigned long adr) ++{ ++ /* TODO: chips with no XIP use should ignore and return */ ++ (void) map_read(map, adr); /* ensure mmu mapping is up to date */ ++ preempt_disable(); ++ local_irq_disable(); ++} ++ ++static void __xipram xip_enable(struct map_info *map, struct flchip *chip, ++ unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ if (chip->state != FL_POINT && chip->state != FL_READY) { ++ map_write(map, CMD(0xff), adr); ++ chip->state = FL_READY; + } ++ (void) map_read(map, adr); ++ asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ ++ local_irq_enable(); ++ preempt_enable(); ++} ++ ++/* ++ * When a delay is required for the flash operation to complete, the ++ * xip_udelay() function is polling for both the given timeout and pending ++ * (but still masked) hardware interrupts. Whenever there is an interrupt ++ * pending then the flash erase or write operation is suspended, array mode ++ * restored and interrupts unmasked. Task scheduling might also happen at that ++ * point. The CPU eventually returns from the interrupt or the call to ++ * schedule() and the suspended flash operation is resumed for the remaining ++ * of the delay period. ++ * ++ * Warning: this function _will_ fool interrupt latency tracing tools. ++ */ ++ ++static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, ++ unsigned long adr, int usec) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_intelext *cfip = cfi->cmdset_priv; ++ map_word status, OK = CMD(0x80); ++ unsigned long suspended, start = xip_currtime(); ++ flstate_t oldstate, newstate; ++ ++ do { ++ cpu_relax(); ++ if (xip_irqpending() && cfip && ++ ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || ++ (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && ++ (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { ++ /* ++ * Let's suspend the erase or write operation when ++ * supported. Note that we currently don't try to ++ * suspend interleaved chips if there is already ++ * another operation suspended (imagine what happens ++ * when one chip was already done with the current ++ * operation while another chip suspended it, then ++ * we resume the whole thing at once). Yes, it ++ * can happen! ++ */ ++ map_write(map, CMD(0xb0), adr); ++ map_write(map, CMD(0x70), adr); ++ usec -= xip_elapsed_since(start); ++ suspended = xip_currtime(); ++ do { ++ if (xip_elapsed_since(suspended) > 100000) { ++ /* ++ * The chip doesn't want to suspend ++ * after waiting for 100 msecs. ++ * This is a critical error but there ++ * is not much we can do here. ++ */ ++ return; ++ } ++ status = map_read(map, adr); ++ } while (!map_word_andequal(map, status, OK, OK)); ++ ++ /* Suspend succeeded */ ++ oldstate = chip->state; ++ if (oldstate == FL_ERASING) { ++ if (!map_word_bitsset(map, status, CMD(0x40))) ++ break; ++ newstate = FL_XIP_WHILE_ERASING; ++ chip->erase_suspended = 1; ++ } else { ++ if (!map_word_bitsset(map, status, CMD(0x04))) ++ break; ++ newstate = FL_XIP_WHILE_WRITING; ++ chip->write_suspended = 1; ++ } ++ chip->state = newstate; ++ map_write(map, CMD(0xff), adr); ++ (void) map_read(map, adr); ++ asm volatile (".rep 8; nop; .endr"); ++ local_irq_enable(); ++ preempt_enable(); ++ asm volatile (".rep 8; nop; .endr"); ++ cond_resched(); ++ ++ /* ++ * We're back. However someone else might have ++ * decided to go write to the chip if we are in ++ * a suspended erase state. If so let's wait ++ * until it's done. ++ */ ++ preempt_disable(); ++ while (chip->state != newstate) { ++ DECLARE_WAITQUEUE(wait, current); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ preempt_enable(); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ preempt_disable(); ++ } ++ /* Disallow XIP again */ ++ local_irq_disable(); ++ ++ /* Resume the write or erase operation */ ++ map_write(map, CMD(0xd0), adr); ++ map_write(map, CMD(0x70), adr); ++ chip->state = oldstate; ++ start = xip_currtime(); ++ } else if (usec >= 1000000/HZ) { ++ /* ++ * Try to save on CPU power when waiting delay ++ * is at least a system timer tick period. ++ * No need to be extremely accurate here. ++ */ ++ xip_cpu_idle(); ++ } ++ status = map_read(map, adr); ++ } while (!map_word_andequal(map, status, OK, OK) ++ && xip_elapsed_since(start) < usec); ++} ++ ++#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) ++ ++/* ++ * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while ++ * the flash is actively programming or erasing since we have to poll for ++ * the operation to complete anyway. We can't do that in a generic way with ++ * a XIP setup so do it before the actual flash operation in this case. ++ */ ++#undef INVALIDATE_CACHED_RANGE ++#define INVALIDATE_CACHED_RANGE(x...) ++#define XIP_INVAL_CACHED_RANGE(map, from, size) \ ++ do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) ++ ++/* ++ * Extra notes: ++ * ++ * Activating this XIP support changes the way the code works a bit. For ++ * example the code to suspend the current process when concurrent access ++ * happens is never executed because xip_udelay() will always return with the ++ * same chip state as it was entered with. This is why there is no care for ++ * the presence of add_wait_queue() or schedule() calls from within a couple ++ * xip_disable()'d areas of code, like in do_erase_oneblock for example. ++ * The queueing and scheduling are always happening within xip_udelay(). ++ * ++ * Similarly, get_chip() and put_chip() just happen to always be executed ++ * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state ++ * is in array mode, therefore never executing many cases therein and not ++ * causing any problem with XIP. ++ */ ++ ++#else ++ ++#define xip_disable(map, chip, adr) ++#define xip_enable(map, chip, adr) ++ ++#define UDELAY(map, chip, adr, usec) cfi_udelay(usec) ++ ++#define XIP_INVAL_CACHED_RANGE(x...) ++ ++#endif ++ ++static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret = 0; ++ ++ adr += chip->start; ++ ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(map_bankwidth(map)-1); ++ ++ spin_lock(chip->mutex); ++ ++ ret = get_chip(map, chip, cmd_addr, FL_POINT); ++ ++ if (!ret) { ++ if (chip->state != FL_POINT && chip->state != FL_READY) ++ map_write(map, CMD(0xff), cmd_addr); + + chip->state = FL_POINT; + chip->ref_point_counter++; ++ } + spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) ++ ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +@@ -380,12 +1038,10 @@ + int chipnum; + int ret = 0; + +- if (from + len > mtd->size) ++ if (!map->virt || (from + len > mtd->size)) + return -EINVAL; + +- *mtdbuf = map->point(map, from, len); +- if(*mtdbuf == NULL) +- return -EINVAL; /* can not point this region */ ++ *mtdbuf = (void *)map->virt + from; + *retlen = 0; + + /* Now lock the chip(s) to POINT state */ +@@ -418,14 +1074,13 @@ + return 0; + } + +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + +- map->unpoint(map, addr, from, len); + /* Now unlock the chip(s) POINT state */ + + /* ofs: offset within the first chip that the first read should start */ +@@ -446,13 +1101,14 @@ + thislen = len; + + spin_lock(chip->mutex); +- if(chip->state == FL_POINT){ ++ if (chip->state == FL_POINT) { + chip->ref_point_counter--; + if(chip->ref_point_counter == 0) + chip->state = FL_READY; + } else +- printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ +- wake_up(&chip->wq); ++ printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ ++ ++ put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + + len -= thislen; +@@ -463,136 +1119,32 @@ + + static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int suspended = 0; + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ +- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); +- +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); ++ cmd_addr = adr & ~(map_bankwidth(map)-1); + +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ +- switch (chip->state) { +- case FL_ERASING: +- if (!cfi->cmdset_priv || +- !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), cmd_addr); +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- // printk("Erase suspending at 0x%lx\n", cmd_addr); +- for (;;) { +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%llx\n", (__u64)status); +- return -EIO; +- } +- ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { + spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); ++ return ret; + } + +- suspended = 1; +- cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; +- break; +- +-#if 0 +- case FL_WRITING: +- /* Not quite yet */ +-#endif +- +- case FL_READY: +- case FL_POINT: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; ++ if (chip->state != FL_POINT && chip->state != FL_READY) { ++ map_write(map, CMD(0xff), cmd_addr); + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; +- break; + } + +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- cfi_write(map, CMD(0x70), cmd_addr); +- } ++ put_chip(map, chip, cmd_addr); + +- wake_up(&chip->wq); + spin_unlock(chip->mutex); + return 0; + } +@@ -636,232 +1188,50 @@ + return ret; + } + +-static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) +-{ +- struct map_info *map = mtd->priv; +- struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp=cfi->cmdset_priv; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int count=len; +- struct flchip *chip; +- int chip_num,offst; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- +- chip=0; +- /* Calculate which chip & protection register offset we need */ +- chip_num=((unsigned int)from/reg_sz); +- offst=from-(reg_sz*chip_num)+base_offst; +- +- while(count){ +- +- if(chip_num>=cfi->numchips) +- goto out; +- +- /* Make sure that the chip is in the right state */ +- +- timeo = jiffies + HZ; +- chip=&cfi->chips[chip_num]; +- retry: +- spin_lock(chip->mutex); +- +- switch (chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- break; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- +- /* Now read the data required from this flash */ +- +- cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); +- while(count && ((offst-base_offst)read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); +- buf++; +- offst++; +- count--; +- } +- +- chip->state=FL_CFI_QUERY; +- spin_unlock(chip->mutex); +- /* Move on to the next chip */ +- chip_num++; +- offst=base_offst; +- +- } +- +- out: +- wake_up(&chip->wq); +- return len-count; +-} +- +-static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +-{ +- struct map_info *map = mtd->priv; +- struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp=cfi->cmdset_priv; +- int base_offst,reg_sz; +- +- /* Check that we actually have some protection registers */ +- if(!(extp->FeatureSupport&64)){ +- printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); +- return 0; +- } +- +- base_offst=(1<FactProtRegSize); +- reg_sz=(1<UserProtRegSize); +- +- return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +-} +- +-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +-{ +- struct map_info *map = mtd->priv; +- struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp=cfi->cmdset_priv; +- int base_offst,reg_sz; +- +- /* Check that we actually have some protection registers */ +- if(!(extp->FeatureSupport&64)){ +- printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); +- return 0; +- } +- +- base_offst=0; +- reg_sz=(1<FactProtRegSize); +- +- return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +-} +- +- +-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum) ++static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ++ unsigned long adr, map_word datum, int mode) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; +- cfi_word status, status_OK; ++ map_word status, status_OK, write_cmd; + unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int z, suspended=0, ret=0; ++ int z, ret=0; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); +- +- timeo = jiffies + HZ; +- retry: +- spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; ++ switch (mode) { ++ case FL_WRITING: write_cmd = CMD(0x40); break; ++ case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; ++ default: return -EINVAL; + } + +- spin_unlock(chip->mutex); +- cfi_udelay(1); + spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; +- +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, mode); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + ++ XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); + ENABLE_VPP(map); +- cfi_write(map, CMD(0x40), adr); +- cfi_write(map, datum, adr); +- chip->state = FL_WRITING; ++ xip_disable(map, chip, adr); ++ map_write(map, write_cmd, adr); ++ map_write(map, datum, adr); ++ chip->state = mode; + + spin_unlock(chip->mutex); +- cfi_udelay(chip->word_write_time); ++ INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); ++ UDELAY(map, chip, adr, chip->word_write_time); + spin_lock(chip->mutex); + + timeo = jiffies + (HZ/2); + z = 0; + for (;;) { +- if (chip->state != FL_WRITING) { ++ if (chip->state != mode) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -872,14 +1242,14 @@ + continue; + } + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); ++ xip_enable(map, chip, adr); + printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); + ret = -EIO; + goto out; +@@ -888,7 +1258,7 @@ + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); + z++; +- cfi_udelay(1); ++ UDELAY(map, chip, adr, 1); + spin_lock(chip->mutex); + } + if (!z) { +@@ -901,34 +1271,20 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; ++ + /* check for lock bit */ +- if (status & CMD(0x02)) { ++ if (map_word_bitsset(map, status, CMD(0x02))) { + /* clear status */ +- cfi_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x50), adr); + /* put back into read status register mode */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } +- out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ + +- wake_up(&chip->wq); ++ xip_enable(map, chip, adr); ++ out: put_chip(map, chip, adr); + spin_unlock(chip->mutex); ++ + return ret; + } + +@@ -949,35 +1305,22 @@ + ofs = to - (chipnum << cfi->chipshift); + + /* If it's not bus-aligned, do the first byte write */ +- if (ofs & (CFIDEV_BUSWIDTH-1)) { +- unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); ++ if (ofs & (map_bankwidth(map)-1)) { ++ unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); + int gap = ofs - bus_ofs; +- int i = 0, n = 0; +- u_char tmp_buf[8]; +- cfi_word datum; +- +- while (gap--) +- tmp_buf[i++] = 0xff; +- while (len && i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = buf[n++], len--; +- while (i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = 0xff; ++ int n; ++ map_word datum; + +- if (cfi_buswidth_is_2()) { +- datum = *(__u16*)tmp_buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)tmp_buf; +- } else if (cfi_buswidth_is_8()) { +- datum = *(__u64*)tmp_buf; +- } else { +- return -EINVAL; /* should never happen, but be safe */ +- } ++ n = min_t(int, len, map_bankwidth(map)-gap); ++ datum = map_word_ff(map); ++ datum = map_word_load_partial(map, datum, buf, gap, n); + + ret = do_write_oneword(map, &cfi->chips[chipnum], +- bus_ofs, datum); ++ bus_ofs, datum, FL_WRITING); + if (ret) + return ret; + ++ len -= n; + ofs += n; + buf += n; + (*retlen) += n; +@@ -990,30 +1333,18 @@ + } + } + +- while(len >= CFIDEV_BUSWIDTH) { +- cfi_word datum; +- +- if (cfi_buswidth_is_1()) { +- datum = *(__u8*)buf; +- } else if (cfi_buswidth_is_2()) { +- datum = *(__u16*)buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)buf; +- } else if (cfi_buswidth_is_8()) { +- datum = *(__u64*)buf; +- } else { +- return -EINVAL; +- } ++ while(len >= map_bankwidth(map)) { ++ map_word datum = map_word_load(map, buf); + + ret = do_write_oneword(map, &cfi->chips[chipnum], +- ofs, datum); ++ ofs, datum, FL_WRITING); + if (ret) + return ret; + +- ofs += CFIDEV_BUSWIDTH; +- buf += CFIDEV_BUSWIDTH; +- (*retlen) += CFIDEV_BUSWIDTH; +- len -= CFIDEV_BUSWIDTH; ++ ofs += map_bankwidth(map); ++ buf += map_bankwidth(map); ++ (*retlen) += map_bankwidth(map); ++ len -= map_bankwidth(map); + + if (ofs >> cfi->chipshift) { + chipnum ++; +@@ -1023,203 +1354,126 @@ + } + } + +- if (len & (CFIDEV_BUSWIDTH-1)) { +- int i = 0, n = 0; +- u_char tmp_buf[8]; +- cfi_word datum; +- +- while (len--) +- tmp_buf[i++] = buf[n++]; +- while (i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = 0xff; ++ if (len & (map_bankwidth(map)-1)) { ++ map_word datum; + +- if (cfi_buswidth_is_2()) { +- datum = *(__u16*)tmp_buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)tmp_buf; +- } else if (cfi_buswidth_is_8()) { +- datum = *(__u64*)tmp_buf; +- } else { +- return -EINVAL; /* should never happen, but be safe */ +- } ++ datum = map_word_ff(map); ++ datum = map_word_load_partial(map, datum, buf, 0, len); + + ret = do_write_oneword(map, &cfi->chips[chipnum], +- ofs, datum); ++ ofs, datum, FL_WRITING); + if (ret) + return ret; + +- (*retlen) += n; ++ (*retlen) += len; + } + + return 0; + } + + +-static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ++static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; +- cfi_word status, status_OK; ++ map_word status, status_OK; + unsigned long cmd_adr, timeo; +- DECLARE_WAITQUEUE(wait, current); +- int wbufsize, z, suspended=0, ret=0; ++ int wbufsize, z, ret=0, bytes, words; + +- wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; + cmd_adr = adr & ~(wbufsize-1); + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) +- break; +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; ++ ret = get_chip(map, chip, cmd_adr, FL_WRITING); ++ if (ret) { + spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; ++ return ret; + } + +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; ++ XIP_INVAL_CACHED_RANGE(map, adr, len); ++ ENABLE_VPP(map); ++ xip_disable(map, chip, cmd_adr); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- /* We know we're now in FL_STATUS mode, and 'status' is current */ + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set + [...], the device will not accept any more Write to Buffer commands". + So we must check here and reset those bits if they're set. Otherwise + we're just pissing in the wind */ +- if (status & CMD(0x30)) { +- printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status); +- cfi_write(map, CMD(0x50), cmd_adr); +- cfi_write(map, CMD(0x70), cmd_adr); ++ if (chip->state != FL_STATUS) ++ map_write(map, CMD(0x70), cmd_adr); ++ status = map_read(map, cmd_adr); ++ if (map_word_bitsset(map, status, CMD(0x30))) { ++ xip_enable(map, chip, cmd_adr); ++ printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); ++ xip_disable(map, chip, cmd_adr); ++ map_write(map, CMD(0x50), cmd_adr); ++ map_write(map, CMD(0x70), cmd_adr); + } +- ENABLE_VPP(map); ++ + chip->state = FL_WRITING_TO_BUFFER; + + z = 0; + for (;;) { +- cfi_write(map, CMD(0xe8), cmd_adr); ++ map_write(map, CMD(0xe8), cmd_adr); + +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + spin_unlock(chip->mutex); +- cfi_udelay(1); ++ UDELAY(map, chip, cmd_adr, 1); + spin_lock(chip->mutex); + + if (++z > 20) { + /* Argh. Not ready for write to buffer */ +- cfi_write(map, CMD(0x70), cmd_adr); ++ map_word Xstatus; ++ map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; +- DISABLE_VPP(map); +- printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr)); ++ Xstatus = map_read(map, cmd_adr); + /* Odd. Clear status bits */ +- cfi_write(map, CMD(0x50), cmd_adr); +- cfi_write(map, CMD(0x70), cmd_adr); ++ map_write(map, CMD(0x50), cmd_adr); ++ map_write(map, CMD(0x70), cmd_adr); ++ xip_enable(map, chip, cmd_adr); ++ printk(KERN_ERR "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n", ++ status.x[0], Xstatus.x[0]); + ret = -EIO; + goto out; + } + } + + /* Write length of data to come */ +- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); ++ bytes = len & (map_bankwidth(map)-1); ++ words = len / map_bankwidth(map); ++ map_write(map, CMD(words - !bytes), cmd_adr ); + + /* Write data */ +- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { +- if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); +- } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); +- } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); +- } else if (cfi_buswidth_is_8()) { +- map->write64 (map, *((__u64*)buf)++, adr+z); +- } else { +- DISABLE_VPP(map); +- ret = -EINVAL; +- goto out; ++ z = 0; ++ while(z < words * map_bankwidth(map)) { ++ map_word datum = map_word_load(map, buf); ++ map_write(map, datum, adr+z); ++ ++ z += map_bankwidth(map); ++ buf += map_bankwidth(map); + } ++ ++ if (bytes) { ++ map_word datum; ++ ++ datum = map_word_ff(map); ++ datum = map_word_load_partial(map, datum, buf, 0, bytes); ++ map_write(map, datum, adr+z); + } ++ + /* GO GO GO */ +- cfi_write(map, CMD(0xd0), cmd_adr); ++ map_write(map, CMD(0xd0), cmd_adr); + chip->state = FL_WRITING; + + spin_unlock(chip->mutex); +- cfi_udelay(chip->buffer_write_time); ++ INVALIDATE_CACHED_RANGE(map, adr, len); ++ UDELAY(map, chip, cmd_adr, chip->buffer_write_time); + spin_lock(chip->mutex); + + timeo = jiffies + (HZ/2); +@@ -1227,6 +1481,7 @@ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -1237,14 +1492,14 @@ + continue; + } + +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); ++ xip_enable(map, chip, cmd_adr); + printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); + ret = -EIO; + goto out; +@@ -1252,7 +1507,7 @@ + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); +- cfi_udelay(1); ++ UDELAY(map, chip, cmd_adr, 1); + z++; + spin_lock(chip->mutex); + } +@@ -1266,33 +1521,18 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; ++ + /* check for lock bit */ +- if (status & CMD(0x02)) { ++ if (map_word_bitsset(map, status, CMD(0x02))) { + /* clear status */ +- cfi_write(map, CMD(0x50), cmd_adr); ++ map_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } +- out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ + +- wake_up(&chip->wq); ++ xip_enable(map, chip, cmd_adr); ++ out: put_chip(map, chip, cmd_adr); + spin_unlock(chip->mutex); + return ret; + } +@@ -1302,7 +1542,7 @@ + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + int ret = 0; + int chipnum; + unsigned long ofs; +@@ -1315,8 +1555,8 @@ + ofs = to - (chipnum << cfi->chipshift); + + /* If it's not bus-aligned, do the first word write */ +- if (ofs & (CFIDEV_BUSWIDTH-1)) { +- size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); ++ if (ofs & (map_bankwidth(map)-1)) { ++ size_t local_len = (-ofs)&(map_bankwidth(map)-1); + if (local_len > len) + local_len = len; + ret = cfi_intelext_write_words(mtd, to, local_len, +@@ -1335,13 +1575,12 @@ + } + } + +- /* Write buffer is worth it only if more than one word to write... */ +- while(len > CFIDEV_BUSWIDTH) { ++ while(len) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) +- size = len & ~(CFIDEV_BUSWIDTH-1); ++ size = len; + ret = do_write_buffer(map, &cfi->chips[chipnum], + ofs, buf, size); + if (ret) +@@ -1359,116 +1598,14 @@ + return 0; + } + } +- +- /* ... and write the remaining bytes */ +- if (len > 0) { +- size_t local_retlen; +- ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift), +- len, &local_retlen, buf); +- if (ret) +- return ret; +- (*retlen) += local_retlen; +- } +- + return 0; + } + +-typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, +- unsigned long adr, void *thunk); +- +-static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, +- loff_t ofs, size_t len, void *thunk) +-{ +- struct map_info *map = mtd->priv; +- struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr; +- int chipnum, ret = 0; +- int i, first; +- struct mtd_erase_region_info *regions = mtd->eraseregions; +- +- if (ofs > mtd->size) +- return -EINVAL; +- +- if ((len + ofs) > mtd->size) +- return -EINVAL; +- +- /* Check that both start and end of the requested erase are +- * aligned with the erasesize at the appropriate addresses. +- */ +- +- i = 0; +- +- /* Skip all erase regions which are ended before the start of +- the requested erase. Actually, to save on the calculations, +- we skip to the first erase region which starts after the +- start of the requested erase, and then go back one. +- */ +- +- while (i < mtd->numeraseregions && ofs >= regions[i].offset) +- i++; +- i--; +- +- /* OK, now i is pointing at the erase region in which this +- erase request starts. Check the start of the requested +- erase range is aligned with the erase size which is in +- effect here. +- */ +- +- if (ofs & (regions[i].erasesize-1)) +- return -EINVAL; +- +- /* Remember the erase region we start on */ +- first = i; +- +- /* Next, check that the end of the requested erase is aligned +- * with the erase region at that address. +- */ +- +- while (inumeraseregions && (ofs + len) >= regions[i].offset) +- i++; +- +- /* As before, drop back one to point at the region in which +- the address actually falls +- */ +- i--; +- +- if ((ofs + len) & (regions[i].erasesize-1)) +- return -EINVAL; +- +- chipnum = ofs >> cfi->chipshift; +- adr = ofs - (chipnum << cfi->chipshift); +- +- i=first; +- +- while(len) { +- ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); +- +- if (ret) +- return ret; +- +- adr += regions[i].erasesize; +- len -= regions[i].erasesize; +- +- if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) +- i++; +- +- if (adr >> cfi->chipshift) { +- adr = 0; +- chipnum++; +- +- if (chipnum >= cfi->numchips) +- break; +- } +- } +- +- return 0; +-} +- +- +-static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) ++static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr, int len, void *thunk) + { + struct cfi_private *cfi = map->fldrv_priv; +- cfi_word status, status_OK; ++ map_word status, status_OK; + unsigned long timeo; + int retries = 3; + DECLARE_WAITQUEUE(wait, current); +@@ -1479,60 +1616,30 @@ + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: ++ retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + ++ XIP_INVAL_CACHED_RANGE(map, adr, len); + ENABLE_VPP(map); ++ xip_disable(map, chip, adr); ++ + /* Clear the status register first */ +- cfi_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x50), adr); + + /* Now erase */ +- cfi_write(map, CMD(0x20), adr); +- cfi_write(map, CMD(0xD0), adr); ++ map_write(map, CMD(0x20), adr); ++ map_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + + spin_unlock(chip->mutex); +- set_current_state(TASK_UNINTERRUPTIBLE); +- schedule_timeout((chip->erase_time*HZ)/(2*1000)); ++ INVALIDATE_CACHED_RANGE(map, adr, len); ++ UDELAY(map, chip, adr, chip->erase_time*1000/2); + spin_lock(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ +@@ -1550,84 +1657,92 @@ + spin_lock(chip->mutex); + continue; + } +- if (chip->oldstate) { ++ if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + } + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { +- cfi_write(map, CMD(0x70), adr); ++ map_word Xstatus; ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n", +- adr, (__u64)status, (__u64)cfi_read(map, adr)); ++ Xstatus = map_read(map, adr); + /* Clear status bits */ +- cfi_write(map, CMD(0x50), adr); +- cfi_write(map, CMD(0x70), adr); +- DISABLE_VPP(map); +- spin_unlock(chip->mutex); +- return -EIO; ++ map_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x70), adr); ++ xip_enable(map, chip, adr); ++ printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n", ++ adr, status.x[0], Xstatus.x[0]); ++ ret = -EIO; ++ goto out; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); +- set_current_state(TASK_UNINTERRUPTIBLE); +- schedule_timeout(1); ++ UDELAY(map, chip, adr, 1000000/HZ); + spin_lock(chip->mutex); + } + +- DISABLE_VPP(map); +- ret = 0; +- + /* We've broken this before. It doesn't hurt to be safe */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- status = cfi_read(map, adr); ++ status = map_read(map, adr); + + /* check for lock bit */ +- if (status & CMD(0x3a)) { +- unsigned char chipstatus = status; +- if (status != CMD(status & 0xff)) { +- int i; +- for (i = 1; i> (cfi->device_type * 8); ++ if (map_word_bitsset(map, status, CMD(0x3a))) { ++ unsigned char chipstatus; ++ ++ /* Reset the error bits */ ++ map_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x70), adr); ++ xip_enable(map, chip, adr); ++ ++ chipstatus = status.x[0]; ++ if (!map_word_equal(map, status, CMD(chipstatus))) { ++ int i, w; ++ for (w=0; w> (cfi->device_type * 8); + } +- printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus); + } +- /* Reset the error bits */ +- cfi_write(map, CMD(0x50), adr); +- cfi_write(map, CMD(0x70), adr); ++ printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", ++ status.x[0], chipstatus); ++ } + + if ((chipstatus & 0x30) == 0x30) { +- printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status); ++ printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); + ret = -EIO; + } else if (chipstatus & 0x02) { + /* Protection bit set */ + ret = -EROFS; + } else if (chipstatus & 0x8) { + /* Voltage */ +- printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%llx\n", (__u64)status); ++ printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); + ret = -EIO; + } else if (chipstatus & 0x20) { + if (retries--) { +- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status); ++ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); + timeo = jiffies + HZ; +- chip->state = FL_STATUS; ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); + goto retry; + } +- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status); ++ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); + ret = -EIO; + } ++ } else { ++ xip_enable(map, chip, adr); ++ ret = 0; + } + +- wake_up(&chip->wq); ++ out: put_chip(map, chip, adr); + spin_unlock(chip->mutex); + return ret; + } +@@ -1640,13 +1755,12 @@ + ofs = instr->addr; + len = instr->len; + +- ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); ++ ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + if (ret) + return ret; + + instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } +@@ -1658,39 +1772,22 @@ + int i; + struct flchip *chip; + int ret = 0; +- DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + +- retry: + spin_lock(chip->mutex); ++ ret = get_chip(map, chip, chip->start, FL_SYNCING); + +- switch(chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: ++ if (!ret) { + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_SYNCING: +- spin_unlock(chip->mutex); +- break; +- +- default: +- /* Not an idle state */ +- add_wait_queue(&chip->wq, &wait); +- +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- +- goto retry; + } ++ spin_unlock(chip->mutex); + } + + /* Unlock the chips again */ +@@ -1709,16 +1806,21 @@ + } + + #ifdef DEBUG_LOCK_BITS +-static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) ++static int __xipram do_printlockstatus_oneblock(struct map_info *map, ++ struct flchip *chip, ++ unsigned long adr, ++ int len, void *thunk) + { + struct cfi_private *cfi = map->fldrv_priv; +- int ofs_factor = cfi->interleave * cfi->device_type; ++ int status, ofs_factor = cfi->interleave * cfi->device_type; + ++ xip_disable(map, chip, adr+(2*ofs_factor)); + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); ++ chip->state = FL_JEDEC_QUERY; ++ status = cfi_read_query(map, adr+(2*ofs_factor)); ++ xip_enable(map, chip, 0); + printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", +- adr, cfi_read_query(map, adr+(2*ofs_factor))); +- cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +- ++ adr, status); + return 0; + } + #endif +@@ -1726,73 +1828,41 @@ + #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) + #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) + +-static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) ++static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr, int len, void *thunk) + { + struct cfi_private *cfi = map->fldrv_priv; +- cfi_word status, status_OK; ++ map_word status, status_OK; + unsigned long timeo = jiffies + HZ; +- DECLARE_WAITQUEUE(wait, current); ++ int ret; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +- cfi_write(map, CMD(0x60), adr); ++ xip_disable(map, chip, adr); + ++ map_write(map, CMD(0x60), adr); + if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { +- cfi_write(map, CMD(0x01), adr); ++ map_write(map, CMD(0x01), adr); + chip->state = FL_LOCKING; + } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { +- cfi_write(map, CMD(0xD0), adr); ++ map_write(map, CMD(0xD0), adr); + chip->state = FL_UNLOCKING; + } else + BUG(); + + spin_unlock(chip->mutex); +- schedule_timeout(HZ); ++ UDELAY(map, chip, adr, 1000000/HZ); + spin_lock(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ +@@ -1801,30 +1871,34 @@ + timeo = jiffies + (HZ*20); + for (;;) { + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { +- cfi_write(map, CMD(0x70), adr); ++ map_word Xstatus; ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr)); +- DISABLE_VPP(map); ++ Xstatus = map_read(map, adr); ++ xip_enable(map, chip, adr); ++ printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n", ++ status.x[0], Xstatus.x[0]); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); +- cfi_udelay(1); ++ UDELAY(map, chip, adr, 1); + spin_lock(chip->mutex); + } + + /* Done and happy. */ + chip->state = FL_STATUS; +- DISABLE_VPP(map); +- wake_up(&chip->wq); ++ xip_enable(map, chip, adr); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); + return 0; + } +@@ -1836,17 +1910,17 @@ + #ifdef DEBUG_LOCK_BITS + printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __FUNCTION__, ofs, len); +- cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ++ cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); + #endif + +- ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, ++ ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, + ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); + + #ifdef DEBUG_LOCK_BITS +- printk(KERN_DEBUG __FUNCTION__ +- "%s: lock status after, ret=%d\n", __FUNCTION__, ret); +- cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ++ printk(KERN_DEBUG "%s: lock status after, ret=%d\n", ++ __FUNCTION__, ret); ++ cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); + #endif + +@@ -1860,22 +1934,281 @@ + #ifdef DEBUG_LOCK_BITS + printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __FUNCTION__, ofs, len); +- cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ++ cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); + #endif + +- ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, ++ ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, + ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); + + #ifdef DEBUG_LOCK_BITS +- printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); +- cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ++ printk(KERN_DEBUG "%s: lock status after, ret=%d\n", ++ __FUNCTION__, ret); ++ cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); + #endif + + return ret; + } + ++#ifdef CONFIG_MTD_OTP ++ ++typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, ++ u_long data_offset, u_char *buf, u_int size, ++ u_long prot_offset, u_int groupno, u_int groupsize); ++ ++static int __xipram ++do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, ++ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret; ++ ++ spin_lock(chip->mutex); ++ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); ++ if (ret) { ++ spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ /* let's ensure we're not reading back cached data from array mode */ ++ if (map->inval_cache) ++ map->inval_cache(map, chip->start + offset, size); ++ ++ xip_disable(map, chip, chip->start); ++ if (chip->state != FL_JEDEC_QUERY) { ++ map_write(map, CMD(0x90), chip->start); ++ chip->state = FL_JEDEC_QUERY; ++ } ++ map_copy_from(map, buf, chip->start + offset, size); ++ xip_enable(map, chip, chip->start); ++ ++ /* then ensure we don't keep OTP data in the cache */ ++ if (map->inval_cache) ++ map->inval_cache(map, chip->start + offset, size); ++ ++ put_chip(map, chip, chip->start); ++ spin_unlock(chip->mutex); ++ return 0; ++} ++ ++static int ++do_otp_write(struct map_info *map, struct flchip *chip, u_long offset, ++ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) ++{ ++ int ret; ++ ++ while (size) { ++ unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); ++ int gap = offset - bus_ofs; ++ int n = min_t(int, size, map_bankwidth(map)-gap); ++ map_word datum = map_word_ff(map); ++ ++ datum = map_word_load_partial(map, datum, buf, gap, n); ++ ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); ++ if (ret) ++ return ret; ++ ++ offset += n; ++ buf += n; ++ size -= n; ++ } ++ ++ return 0; ++} ++ ++static int ++do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, ++ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ map_word datum; ++ ++ /* make sure area matches group boundaries */ ++ if (size != grpsz) ++ return -EXDEV; ++ ++ datum = map_word_ff(map); ++ datum = map_word_clr(map, datum, CMD(1 << grpno)); ++ return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); ++} ++ ++static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, ++ otp_op_t action, int user_regs) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_intelext *extp = cfi->cmdset_priv; ++ struct flchip *chip; ++ struct cfi_intelext_otpinfo *otp; ++ u_long devsize, reg_prot_offset, data_offset; ++ u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; ++ u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; ++ int ret; ++ ++ *retlen = 0; ++ ++ /* Check that we actually have some OTP registers */ ++ if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) ++ return -ENODATA; ++ ++ /* we need real chips here not virtual ones */ ++ devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; ++ chip_step = devsize >> cfi->chipshift; ++ ++ for (chip_num = 0; chip_num < cfi->numchips; chip_num += chip_step) { ++ chip = &cfi->chips[chip_num]; ++ otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; ++ ++ /* first OTP region */ ++ field = 0; ++ reg_prot_offset = extp->ProtRegAddr; ++ reg_fact_groups = 1; ++ reg_fact_size = 1 << extp->FactProtRegSize; ++ reg_user_groups = 1; ++ reg_user_size = 1 << extp->UserProtRegSize; ++ ++ while (len > 0) { ++ /* flash geometry fixup */ ++ data_offset = reg_prot_offset + 1; ++ data_offset *= cfi->interleave * cfi->device_type; ++ reg_prot_offset *= cfi->interleave * cfi->device_type; ++ reg_fact_size *= cfi->interleave; ++ reg_user_size *= cfi->interleave; ++ ++ if (user_regs) { ++ groups = reg_user_groups; ++ groupsize = reg_user_size; ++ /* skip over factory reg area */ ++ groupno = reg_fact_groups; ++ data_offset += reg_fact_groups * reg_fact_size; ++ } else { ++ groups = reg_fact_groups; ++ groupsize = reg_fact_size; ++ groupno = 0; ++ } ++ ++ while (len > 0 && groups > 0) { ++ if (!action) { ++ /* ++ * Special case: if action is NULL ++ * we fill buf with otp_info records. ++ */ ++ struct otp_info *otpinfo; ++ map_word lockword; ++ len -= sizeof(struct otp_info); ++ if (len <= 0) ++ return -ENOSPC; ++ ret = do_otp_read(map, chip, ++ reg_prot_offset, ++ (u_char *)&lockword, ++ map_bankwidth(map), ++ 0, 0, 0); ++ if (ret) ++ return ret; ++ otpinfo = (struct otp_info *)buf; ++ otpinfo->start = from; ++ otpinfo->length = groupsize; ++ otpinfo->locked = ++ !map_word_bitsset(map, lockword, ++ CMD(1 << groupno)); ++ from += groupsize; ++ buf += sizeof(*otpinfo); ++ *retlen += sizeof(*otpinfo); ++ } else if (from >= groupsize) { ++ from -= groupsize; ++ data_offset += groupsize; ++ } else { ++ int size = groupsize; ++ data_offset += from; ++ size -= from; ++ from = 0; ++ if (size > len) ++ size = len; ++ ret = action(map, chip, data_offset, ++ buf, size, reg_prot_offset, ++ groupno, groupsize); ++ if (ret < 0) ++ return ret; ++ buf += size; ++ len -= size; ++ *retlen += size; ++ data_offset += size; ++ } ++ groupno++; ++ groups--; ++ } ++ ++ /* next OTP region */ ++ if (++field == extp->NumProtectionFields) ++ break; ++ reg_prot_offset = otp->ProtRegAddr; ++ reg_fact_groups = otp->FactGroups; ++ reg_fact_size = 1 << otp->FactProtRegSize; ++ reg_user_groups = otp->UserGroups; ++ reg_user_size = 1 << otp->UserProtRegSize; ++ otp++; ++ } ++ } ++ ++ return 0; ++} ++ ++static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, ++ size_t len, size_t *retlen, ++ u_char *buf) ++{ ++ return cfi_intelext_otp_walk(mtd, from, len, retlen, ++ buf, do_otp_read, 0); ++} ++ ++static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, ++ size_t len, size_t *retlen, ++ u_char *buf) ++{ ++ return cfi_intelext_otp_walk(mtd, from, len, retlen, ++ buf, do_otp_read, 1); ++} ++ ++static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, ++ size_t len, size_t *retlen, ++ u_char *buf) ++{ ++ return cfi_intelext_otp_walk(mtd, from, len, retlen, ++ buf, do_otp_write, 1); ++} ++ ++static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, ++ loff_t from, size_t len) ++{ ++ size_t retlen; ++ return cfi_intelext_otp_walk(mtd, from, len, &retlen, ++ NULL, do_otp_lock, 1); ++} ++ ++static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, ++ struct otp_info *buf, size_t len) ++{ ++ size_t retlen; ++ int ret; ++ ++ ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); ++ return ret ? : retlen; ++} ++ ++static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, ++ struct otp_info *buf, size_t len) ++{ ++ size_t retlen; ++ int ret; ++ ++ ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); ++ return ret ? : retlen; ++} ++ ++#endif ++ + static int cfi_intelext_suspend(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1889,22 +2222,32 @@ + + spin_lock(chip->mutex); + +- switch(chip->state) { ++ switch (chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: ++ if (chip->oldstate == FL_READY) { + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_PM_SUSPENDED: ++ } else { ++ /* There seems to be an operation pending. We must wait for it. */ ++ printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate); ++ ret = -EAGAIN; ++ } + break; +- + default: ++ /* Should we actually wait? Once upon a time these routines weren't ++ allowed to. Or should we return -EAGAIN, because the upper layers ++ ought to have already shut down anything which was using the device ++ anyway? The latter for now. */ ++ printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate); + ret = -EAGAIN; ++ case FL_PM_SUSPENDED: + break; + } + spin_unlock(chip->mutex); +@@ -1923,6 +2266,7 @@ + because we're returning failure, and it didn't + get power cycled */ + chip->state = chip->oldstate; ++ chip->oldstate = FL_READY; + wake_up(&chip->wq); + } + spin_unlock(chip->mutex); +@@ -1947,8 +2291,8 @@ + + /* Go to known state. Chip may have been power cycled */ + if (chip->state == FL_PM_SUSPENDED) { +- cfi_write(map, CMD(0xFF), 0); +- chip->state = FL_READY; ++ map_write(map, CMD(0xFF), cfi->chips[i].start); ++ chip->oldstate = chip->state = FL_READY; + wake_up(&chip->wq); + } + +@@ -1962,6 +2306,7 @@ + struct cfi_private *cfi = map->fldrv_priv; + kfree(cfi->cmdset_priv); + kfree(cfi->cfiq); ++ kfree(cfi->chips[0].priv); + kfree(cfi); + kfree(mtd->eraseregions); + } +@@ -1969,7 +2314,7 @@ + static char im_name_1[]="cfi_cmdset_0001"; + static char im_name_3[]="cfi_cmdset_0003"; + +-int __init cfi_intelext_init(void) ++static int __init cfi_intelext_init(void) + { + inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001); + inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001); +--- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0002.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -3,19 +3,26 @@ + * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) + * + * Copyright (C) 2000 Crossnet Co. ++ * Copyright (C) 2004 Arcom Control Systems Ltd + * + * 2_by_8 routines added by Simon Munton + * ++ * 4_by_16 work by Carolyn J. Smith ++ * ++ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com ++ * + * This code is GPL + * +- * $Id$ ++ * $Id$ + * + */ + ++#include + #include + #include + #include + #include ++#include + #include + #include + +@@ -23,15 +30,24 @@ + #include + #include + #include ++#include + #include ++#include + #include + + #define AMD_BOOTLOC_BUG ++#define FORCE_WORD_WRITE 0 ++ ++#define MAX_WORD_RETRIES 3 ++ ++#define MANUFACTURER_AMD 0x0001 ++#define MANUFACTURER_SST 0x00BF ++#define SST49LF004B 0x0060 + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); +-static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *); + static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); + static void cfi_amdstd_sync (struct mtd_info *); + static int cfi_amdstd_suspend (struct mtd_info *); +@@ -41,59 +57,213 @@ + static void cfi_amdstd_destroy(struct mtd_info *); + + struct mtd_info *cfi_cmdset_0002(struct map_info *, int); +-static struct mtd_info *cfi_amdstd_setup (struct map_info *); ++static struct mtd_info *cfi_amdstd_setup (struct mtd_info *); + ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); ++#include "fwh_lock.h" + + static struct mtd_chip_driver cfi_amdstd_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_amdstd_destroy, +- name: "cfi_cmdset_0002", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_amdstd_destroy, ++ .name = "cfi_cmdset_0002", ++ .module = THIS_MODULE + }; + +-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++ ++/* #define DEBUG_CFI_FEATURES */ ++ ++ ++#ifdef DEBUG_CFI_FEATURES ++static void cfi_tell_features(struct cfi_pri_amdstd *extp) + { +- struct cfi_private *cfi = map->fldrv_priv; +- unsigned char bootloc; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int i; +- __u8 major, minor; +- __u32 base = cfi->chips[0].start; ++ const char* erase_suspend[3] = { ++ "Not supported", "Read only", "Read/write" ++ }; ++ const char* top_bottom[6] = { ++ "No WP", "8x8KiB sectors at top & bottom, no WP", ++ "Bottom boot", "Top boot", ++ "Uniform, Bottom WP", "Uniform, Top WP" ++ }; + +- if (cfi->cfi_mode==CFI_MODE_CFI){ +- __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); ++ printk(" Address sensitive unlock: %s\n", ++ (extp->SiliconRevision & 1) ? "Not required" : "Required"); + +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) ++ printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); ++ else ++ printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); + +- major = cfi_read_query(map, base + (adr+3)*ofs_factor); +- minor = cfi_read_query(map, base + (adr+4)*ofs_factor); ++ if (extp->BlkProt == 0) ++ printk(" Block protection: Not supported\n"); ++ else ++ printk(" Block protection: %d sectors per group\n", extp->BlkProt); + +- printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", +- major, minor, adr); +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); + +- cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi->mfr = cfi_read_query(map, base); +- cfi->id = cfi_read_query(map, base + ofs_factor); ++ printk(" Temporary block unprotect: %s\n", ++ extp->TmpBlkUnprotect ? "Supported" : "Not supported"); ++ printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); ++ printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); ++ printk(" Burst mode: %s\n", ++ extp->BurstMode ? "Supported" : "Not supported"); ++ if (extp->PageMode == 0) ++ printk(" Page mode: Not supported\n"); ++ else ++ printk(" Page mode: %d word page\n", extp->PageMode << 2); ++ ++ printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMin >> 4, extp->VppMin & 0xf); ++ printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMax >> 4, extp->VppMax & 0xf); ++ ++ if (extp->TopBottom < ARRAY_SIZE(top_bottom)) ++ printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); ++ else ++ printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); ++} ++#endif + +- /* Wheee. Bring me the head of someone at AMD. */ + #ifdef AMD_BOOTLOC_BUG ++/* Wheee. Bring me the head of someone at AMD. */ ++static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ __u8 major = extp->MajorVersion; ++ __u8 minor = extp->MinorVersion; ++ + if (((major << 8) | minor) < 0x3131) { + /* CFI version 1.0 => don't trust bootloc */ + if (cfi->id & 0x80) { + printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); +- bootloc = 3; /* top boot */ ++ extp->TopBottom = 3; /* top boot */ + } else { +- bootloc = 2; /* bottom boot */ ++ extp->TopBottom = 2; /* bottom boot */ + } +- } else ++ } ++} + #endif +- { +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); ++ ++static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ if (cfi->cfiq->BufWriteTimeoutTyp) { ++ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); ++ mtd->write = cfi_amdstd_write_buffers; ++ } ++} ++ ++static void fixup_use_secsi(struct mtd_info *mtd, void *param) ++{ ++ /* Setup for chips with a secsi area */ ++ mtd->read_user_prot_reg = cfi_amdstd_secsi_read; ++ mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; ++} ++ ++static void fixup_use_erase_chip(struct mtd_info *mtd, void *param) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ if ((cfi->cfiq->NumEraseRegions == 1) && ++ ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { ++ mtd->erase = cfi_amdstd_erase_chip; ++ } ++ ++} ++ ++static struct cfi_fixup cfi_fixup_table[] = { ++#ifdef AMD_BOOTLOC_BUG ++ { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, ++#endif ++ { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, ++ { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, ++ { CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, }, ++ { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, ++ { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, ++ { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, ++#if !FORCE_WORD_WRITE ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, ++#endif ++ { 0, 0, NULL, NULL } ++}; ++static struct cfi_fixup jedec_fixup_table[] = { ++ { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, ++ { 0, 0, NULL, NULL } ++}; ++ ++static struct cfi_fixup fixup_table[] = { ++ /* The CFI vendor ids and the JEDEC vendor IDs appear ++ * to be common. It is like the devices id's are as ++ * well. This table is to pick all cases where ++ * we know that is the case. ++ */ ++ { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL }, ++ { 0, 0, NULL, NULL } ++}; ++ ++ ++struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct mtd_info *mtd; ++ int i; ++ ++ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); ++ if (!mtd) { ++ printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); ++ return NULL; ++ } ++ memset(mtd, 0, sizeof(*mtd)); ++ mtd->priv = map; ++ mtd->type = MTD_NORFLASH; ++ ++ /* Fill in the default mtd operations */ ++ mtd->erase = cfi_amdstd_erase_varsize; ++ mtd->write = cfi_amdstd_write_words; ++ mtd->read = cfi_amdstd_read; ++ mtd->sync = cfi_amdstd_sync; ++ mtd->suspend = cfi_amdstd_suspend; ++ mtd->resume = cfi_amdstd_resume; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->name = map->name; ++ ++ if (cfi->cfi_mode==CFI_MODE_CFI){ ++ unsigned char bootloc; ++ /* ++ * It's a real CFI chip, not one for which the probe ++ * routine faked a CFI structure. So we read the feature ++ * table from it. ++ */ ++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ struct cfi_pri_amdstd *extp; ++ ++ extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); ++ if (!extp) { ++ kfree(mtd); ++ return NULL; ++ } ++ ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; ++ ++ /* Apply cfi device specific fixups */ ++ cfi_fixup(mtd, cfi_fixup_table); ++ ++#ifdef DEBUG_CFI_FEATURES ++ /* Tell the user about it in lots of lovely detail */ ++ cfi_tell_features(extp); ++#endif ++ ++ bootloc = extp->TopBottom; ++ if ((bootloc != 2) && (bootloc != 3)) { ++ printk(KERN_WARNING "%s: CFI does not contain boot " ++ "bank location. Assuming top.\n", map->name); ++ bootloc = 2; + } ++ + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + +@@ -106,29 +276,28 @@ + cfi->cfiq->EraseRegionInfo[j] = swap; + } + } +- switch (cfi->device_type) { +- case CFI_DEVICETYPE_X8: ++ /* Set the default CFI lock/unlock addresses */ + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; +- break; +- case CFI_DEVICETYPE_X16: ++ /* Modify the unlock address if we are in compatibility mode */ ++ if ( /* x16 in x8 mode */ ++ ((cfi->device_type == CFI_DEVICETYPE_X8) && ++ (cfi->cfiq->InterfaceDesc == 2)) || ++ /* x32 in x16 mode */ ++ ((cfi->device_type == CFI_DEVICETYPE_X16) && ++ (cfi->cfiq->InterfaceDesc == 4))) ++ { + cfi->addr_unlock1 = 0xaaa; +- if (map->buswidth == cfi->interleave) { +- /* X16 chip(s) in X8 mode */ + cfi->addr_unlock2 = 0x555; +- } else { +- cfi->addr_unlock2 = 0x554; +- } +- break; +- case CFI_DEVICETYPE_X32: +- cfi->addr_unlock1 = 0x1555; +- cfi->addr_unlock2 = 0xaaa; +- break; +- default: +- printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type); +- return NULL; + } ++ + } /* CFI mode */ ++ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { ++ /* Apply jedec specific fixups */ ++ cfi_fixup(mtd, jedec_fixup_table); ++ } ++ /* Apply generic fixups */ ++ cfi_fixup(mtd, fixup_table); + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; +@@ -138,40 +307,26 @@ + + map->fldrv = &cfi_amdstd_chipdrv; + +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); +- return cfi_amdstd_setup(map); ++ return cfi_amdstd_setup(mtd); + } + +-static struct mtd_info *cfi_amdstd_setup(struct map_info *map) ++ ++static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) + { ++ struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- struct mtd_info *mtd; + unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; ++ unsigned long offset = 0; ++ int i,j; + +- mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + printk(KERN_NOTICE "number of %s chips: %d\n", + (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); +- +- if (!mtd) { +- printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); +- goto setup_err; +- } +- +- memset(mtd, 0, sizeof(*mtd)); +- mtd->priv = map; +- mtd->type = MTD_NORFLASH; +- /* Also select the correct geometry setup too */ ++ /* Select the correct geometry setup */ + mtd->size = devsize * cfi->numchips; + +- if (cfi->cfiq->NumEraseRegions == 1) { +- /* No need to muck about with multiple erase sizes */ +- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; +- } else { +- unsigned long offset = 0; +- int i,j; +- + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; +- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); ++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) ++ * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); + goto setup_err; +@@ -206,67 +361,12 @@ + mtd->eraseregions[i].numblocks); + } + #endif +- } +- +- switch (CFIDEV_BUSWIDTH) +- { +- case 1: +- case 2: +- case 4: +-#if 1 +- if (mtd->numeraseregions > 1) +- mtd->erase = cfi_amdstd_erase_varsize; +- else +-#endif +- if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) +- mtd->erase = cfi_amdstd_erase_chip; +- else +- mtd->erase = cfi_amdstd_erase_onesize; +- mtd->read = cfi_amdstd_read; +- mtd->write = cfi_amdstd_write; +- break; +- +- default: +- printk(KERN_WARNING "Unsupported buswidth\n"); +- goto setup_err; +- break; +- } +- if (cfi->fast_prog) { +- /* In cfi_amdstd_write() we frob the protection stuff +- without paying any attention to the state machine. +- This upsets in-progress erases. So we turn this flag +- off for now till the code gets fixed. */ +- printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); +- cfi->fast_prog = 0; +- } +- +- +- /* does this chip have a secsi area? */ +- if(cfi->mfr==1){ +- +- switch(cfi->id){ +- case 0x50: +- case 0x53: +- case 0x55: +- case 0x56: +- case 0x5C: +- case 0x5F: +- /* Yes */ +- mtd->read_user_prot_reg = cfi_amdstd_secsi_read; +- mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; +- default: +- ; +- } +- } + ++ /* FIXME: erase-suspend-program is broken. See ++ http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ ++ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); + +- mtd->sync = cfi_amdstd_sync; +- mtd->suspend = cfi_amdstd_suspend; +- mtd->resume = cfi_amdstd_resume; +- mtd->flags = MTD_CAP_NORFLASH; +- map->fldrv = &cfi_amdstd_chipdrv; +- mtd->name = map->name; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -280,46 +380,182 @@ + return NULL; + } + +-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++/* ++ * Return true if the chip is ready. ++ * ++ * Ready is one of: read mode, query mode, erase-suspend-read mode (in any ++ * non-suspended sector) and is indicated by no toggle bits toggling. ++ * ++ * Note that anything more complicated than checking if no bits are toggling ++ * (including checking DQ5 for an error status) is tricky to get working ++ * correctly and is therefore not done (particulary with interleaved chips ++ * as each chip must be checked independantly of the others). ++ */ ++static int chip_ready(struct map_info *map, unsigned long addr) ++{ ++ map_word d, t; ++ ++ d = map_read(map, addr); ++ t = map_read(map, addr); ++ ++ return map_word_equal(map, d, t); ++} ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) + { + DECLARE_WAITQUEUE(wait, current); +- unsigned long timeo = jiffies + HZ; ++ struct cfi_private *cfi = map->fldrv_priv; ++ unsigned long timeo; ++ struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; + ++ resettime: ++ timeo = jiffies + HZ; + retry: ++ switch (chip->state) { ++ ++ case FL_STATUS: ++ for (;;) { ++ if (chip_ready(map, adr)) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); ++ cfi_spin_unlock(chip->mutex); ++ return -EIO; ++ } ++ cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); + cfi_spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } + +- if (chip->state != FL_READY){ +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ case FL_READY: ++ case FL_CFI_QUERY: ++ case FL_JEDEC_QUERY: ++ return 0; ++ ++ case FL_ERASING: ++ if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ ++ goto sleep; ++ ++ if (!(mode == FL_READY || mode == FL_POINT ++ || !cfip ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) ++ goto sleep; ++ ++ /* We could check to see if we're trying to access the sector ++ * that is currently being erased. However, no user will try ++ * anything like that so we just wait for the timeout. */ ++ ++ /* Erase suspend */ ++ /* It's harmless to issue the Erase-Suspend and Erase-Resume ++ * commands when the erase algorithm isn't in progress. */ ++ map_write(map, CMD(0xB0), chip->in_progress_block_addr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ if (chip_ready(map, adr)) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ /* Should have suspended the erase by now. ++ * Send an Erase-Resume command as either ++ * there was an error (so leave the erase ++ * routine to recover from it) or we trying to ++ * use the erase-in-progress sector. */ ++ map_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); ++ return -EIO; ++ } + + cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ cfi_spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_READY; ++ return 0; + ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; ++ ++ default: ++ sleep: ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; ++ cfi_spin_lock(chip->mutex); ++ goto resettime; ++ } ++} + +- goto retry; ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ map_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); + } ++ wake_up(&chip->wq); ++} ++ ++ ++static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(map_bankwidth(map)-1); ++ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ if (chip->state != FL_POINT && chip->state != FL_READY) { ++ map_write(map, CMD(0xf0), cmd_addr); + chip->state = FL_READY; ++ } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); ++ put_chip(map, chip, cmd_addr); + ++ cfi_spin_unlock(chip->mutex); + return 0; + } + ++ + static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { + struct map_info *map = mtd->priv; +@@ -361,6 +597,7 @@ + return ret; + } + ++ + static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { + DECLARE_WAITQUEUE(wait, current); +@@ -398,7 +635,7 @@ + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); +@@ -454,125 +691,118 @@ + return ret; + } + +-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) ++ ++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) + { +- unsigned long timeo = jiffies + HZ; +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); ++ unsigned long timeo = jiffies + HZ; ++ /* ++ * We use a 1ms + 1 jiffies generic timeout for writes (most devices ++ * have a max write time of a few hundreds usec). However, we should ++ * use the maximum timeout value given by the chip at probe time ++ * instead. Unfortunately, struct flchip does have a field for ++ * maximum timeout, only for typical which can be far too short ++ * depending of the conditions. The ' + 1' is to avoid having a ++ * timeout of 0 jiffies if HZ is smaller than 1000. ++ */ ++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + int ret = 0; ++ map_word oldd; ++ int retry_cnt = 0; + +- retry: +- cfi_spin_lock(chip->mutex); +- +- if (chip->state != FL_READY) { +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr += chip->start; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- printk(KERN_DEBUG "Wake up to write:\n"); +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; +- +- goto retry; ++ return ret; + } + +- chip->state = FL_WRITING; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", ++ __func__, adr, datum.x[0] ); + +- adr += chip->start; +- ENABLE_VPP(map); +- if (fast) { /* Unlock bypass */ +- cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); +- } +- else { +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ /* ++ * Check for a NOP for the case when the datum to write is already ++ * present - it saves time and works around buggy chips that corrupt ++ * data at other locations when 0xff is written to a location that ++ * already contains 0xff. ++ */ ++ oldd = map_read(map, adr); ++ if (map_word_equal(map, oldd, datum)) { ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n", ++ __func__); ++ goto op_done; + } + +- cfi_write(map, datum, adr); ++ ENABLE_VPP(map); ++ retry: ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ map_write(map, datum, adr); ++ chip->state = FL_WRITING; + + cfi_spin_unlock(chip->mutex); + cfi_udelay(chip->word_write_time); + cfi_spin_lock(chip->mutex); + +- /* Polling toggle bits instead of reading back many times +- This ensures that write operation is really completed, +- or tells us why it failed. */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); +- timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */ +- +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- +- while( (status & dq6) != (oldstatus & dq6) && +- (status & dq5) != dq5 && +- !time_after(jiffies, timeo) ) { ++ /* See comment above for timeout value. */ ++ timeo = jiffies + uWriteTimeout; ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); + +- if (need_resched()) { ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); + cfi_spin_unlock(chip->mutex); +- yield(); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ timeo = jiffies + (HZ / 2); /* FIXME */ + cfi_spin_lock(chip->mutex); +- } else +- udelay(1); +- +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); ++ continue; + } + +- if( (status & dq6) != (oldstatus & dq6) ) { +- /* The erasing didn't stop?? */ +- if( (status & dq5) == dq5 ) { +- /* When DQ5 raises, we must check once again +- if DQ6 is toggling. If not, the erase has been +- completed OK. If not, reset chip. */ +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); ++ if (chip_ready(map, adr)) ++ goto op_done; + +- if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) { +- printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" ); +- } else { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" ); +- } +- } else { +- printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword."); ++ if (time_after(jiffies, timeo)) ++ break; + +- chip->state = FL_READY; +- wake_up(&chip->wq); ++ /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- ret = -EIO; +- } ++ cfi_udelay(1); ++ cfi_spin_lock(chip->mutex); + } + +- DISABLE_VPP(map); ++ printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); ++ ++ /* reset on all failures. */ ++ map_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ if (++retry_cnt <= MAX_WORD_RETRIES) ++ goto retry; ++ ++ ret = -EIO; ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + + return ret; + } + +-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) ++ ++static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs, chipstart; ++ DECLARE_WAITQUEUE(wait, current); + + *retlen = 0; + if (!len) +@@ -583,33 +813,52 @@ + chipstart = cfi->chips[chipnum].start; + + /* If it's not bus-aligned, do the first byte write */ +- if (ofs & (CFIDEV_BUSWIDTH-1)) { +- unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); ++ if (ofs & (map_bankwidth(map)-1)) { ++ unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); + int i = ofs - bus_ofs; + int n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ map_word tmp_buf; + +- map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +- while (len && i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = buf[n++], len--; ++ retry: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); + +- if (cfi_buswidth_is_2()) { +- datum = *(__u16*)tmp_buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)tmp_buf; +- } else { +- return -EINVAL; /* should never happen, but be safe */ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry; + } + ++ /* Load 'tmp_buf' with old contents of flash */ ++ tmp_buf = map_read(map, bus_ofs+chipstart); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ /* Number of bytes to copy from buffer */ ++ n = min_t(int, len, map_bankwidth(map)-i); ++ ++ tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n); ++ + ret = do_write_oneword(map, &cfi->chips[chipnum], +- bus_ofs, datum, 0); ++ bus_ofs, tmp_buf); + if (ret) + return ret; + + ofs += n; + buf += n; + (*retlen) += n; ++ len -= n; + + if (ofs >> cfi->chipshift) { + chipnum ++; +@@ -619,505 +868,457 @@ + } + } + +- if (cfi->fast_prog) { +- /* Go into unlock bypass mode */ +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- } +- + /* We are now aligned, write as much as possible */ +- while(len >= CFIDEV_BUSWIDTH) { +- __u32 datum; ++ while(len >= map_bankwidth(map)) { ++ map_word datum; ++ ++ datum = map_word_load(map, buf); + +- if (cfi_buswidth_is_1()) { +- datum = *(__u8*)buf; +- } else if (cfi_buswidth_is_2()) { +- datum = *(__u16*)buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)buf; +- } else { +- return -EINVAL; +- } + ret = do_write_oneword(map, &cfi->chips[chipnum], +- ofs, datum, cfi->fast_prog); +- if (ret) { +- if (cfi->fast_prog){ +- /* Get out of unlock bypass mode */ +- cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +- } ++ ofs, datum); ++ if (ret) + return ret; +- } + +- ofs += CFIDEV_BUSWIDTH; +- buf += CFIDEV_BUSWIDTH; +- (*retlen) += CFIDEV_BUSWIDTH; +- len -= CFIDEV_BUSWIDTH; ++ ofs += map_bankwidth(map); ++ buf += map_bankwidth(map); ++ (*retlen) += map_bankwidth(map); ++ len -= map_bankwidth(map); + + if (ofs >> cfi->chipshift) { +- if (cfi->fast_prog){ +- /* Get out of unlock bypass mode */ +- cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +- } +- + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + chipstart = cfi->chips[chipnum].start; +- if (cfi->fast_prog){ +- /* Go into unlock bypass mode for next set of chips */ +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); +- } + } + } + +- if (cfi->fast_prog){ +- /* Get out of unlock bypass mode */ +- cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +- } +- + /* Write the trailing bytes if any */ +- if (len & (CFIDEV_BUSWIDTH-1)) { +- int i = 0, n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ if (len & (map_bankwidth(map)-1)) { ++ map_word tmp_buf; + +- map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +- while (len--) +- tmp_buf[i++] = buf[n++]; ++ retry1: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); + +- if (cfi_buswidth_is_2()) { +- datum = *(__u16*)tmp_buf; +- } else if (cfi_buswidth_is_4()) { +- datum = *(__u32*)tmp_buf; +- } else { +- return -EINVAL; /* should never happen, but be safe */ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry1; + } + ++ tmp_buf = map_read(map, ofs + chipstart); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); ++ + ret = do_write_oneword(map, &cfi->chips[chipnum], +- ofs, datum, 0); ++ ofs, tmp_buf); + if (ret) + return ret; + +- (*retlen) += n; ++ (*retlen) += len; + } + + return 0; + } + +-static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ++ ++/* ++ * FIXME: interleaved mode not tested, and probably not supported! ++ */ ++static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ++ unsigned long adr, const u_char *buf, int len) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; +- unsigned int adr; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); ++ unsigned long timeo = jiffies + HZ; ++ /* see comments in do_write_oneword() regarding uWriteTimeo. */ ++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; ++ int ret = -EIO; ++ unsigned long cmd_adr; ++ int z, words; ++ map_word datum; ++ ++ adr += chip->start; ++ cmd_adr = adr; + +- retry: + cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ datum = map_word_load(map, buf); + +- cfi_spin_unlock(chip->mutex); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", ++ __func__, adr, datum.x[0] ); + +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; ++ ENABLE_VPP(map); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + +- goto retry; +- } ++ /* Write Buffer Load */ ++ map_write(map, CMD(0x25), cmd_adr); + +- chip->state = FL_ERASING; ++ chip->state = FL_WRITING_TO_BUFFER; + +- /* Handle devices with one erase region, that only implement +- * the chip erase command. +- */ +- ENABLE_VPP(map); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- timeo = jiffies + (HZ*20); +- adr = cfi->addr_unlock1; ++ /* Write length of data to come */ ++ words = len / map_bankwidth(map); ++ map_write(map, CMD(words - 1), cmd_adr); ++ /* Write data */ ++ z = 0; ++ while(z < words * map_bankwidth(map)) { ++ datum = map_word_load(map, buf); ++ map_write(map, datum, adr + z); + +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. +- */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++ z += map_bankwidth(map); ++ buf += map_bankwidth(map); ++ } ++ z -= map_bankwidth(map); + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ adr += z; ++ ++ /* Write Buffer Program Confirm: GO GO GO */ ++ map_write(map, CMD(0x29), cmd_adr); ++ chip->state = FL_WRITING; + +- /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ cfi_udelay(chip->buffer_write_time); + cfi_spin_lock(chip->mutex); + +- if (chip->state != FL_ERASING) { +- /* Someone's suspended the erase. Sleep */ ++ timeo = jiffies + uWriteTimeout; ++ ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk("erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ ++ timeo = jiffies + (HZ / 2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ if (chip_ready(map, adr)) ++ goto op_done; ++ ++ if( time_after(jiffies, timeo)) ++ break; + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- + cfi_udelay(1); +- + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- } +- if ((status & dq6) != (oldstatus & dq6)) { +- /* The erasing didn't stop?? */ +- if ((status & dq5) == dq5) { +- /* dq5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- } +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- printk("waiting for erase to complete timed out."); +- DISABLE_VPP(map); +- return -EIO; +- } +- DISABLE_VPP(map); ++ ++ printk(KERN_WARNING "MTD %s(): software timeout\n", ++ __func__ ); ++ ++ /* reset on all failures. */ ++ map_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ ++ ret = -EIO; ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + +- return 0; ++ return ret; + } + +-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) ++ ++static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; ++ struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); ++ int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; ++ int ret = 0; ++ int chipnum; ++ unsigned long ofs; + +- retry: +- cfi_spin_lock(chip->mutex); ++ *retlen = 0; ++ if (!len) ++ return 0; + +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ chipnum = to >> cfi->chipshift; ++ ofs = to - (chipnum << cfi->chipshift); + +- cfi_spin_unlock(chip->mutex); ++ /* If it's not bus-aligned, do the first word write */ ++ if (ofs & (map_bankwidth(map)-1)) { ++ size_t local_len = (-ofs)&(map_bankwidth(map)-1); ++ if (local_len > len) ++ local_len = len; ++ ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), ++ local_len, retlen, buf); ++ if (ret) ++ return ret; ++ ofs += local_len; ++ buf += local_len; ++ len -= local_len; + +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } ++ } + +- goto retry; ++ /* Write buffer is worth it only if more than one word to write... */ ++ while (len >= map_bankwidth(map) * 2) { ++ /* We must not cross write block boundaries */ ++ int size = wbufsize - (ofs & (wbufsize-1)); ++ ++ if (size > len) ++ size = len; ++ if (size % map_bankwidth(map)) ++ size -= size % map_bankwidth(map); ++ ++ ret = do_write_buffer(map, &cfi->chips[chipnum], ++ ofs, buf, size); ++ if (ret) ++ return ret; ++ ++ ofs += size; ++ buf += size; ++ (*retlen) += size; ++ len -= size; ++ ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } + } + +- chip->state = FL_ERASING; ++ if (len) { ++ size_t retlen_dregs = 0; + +- adr += chip->start; +- ENABLE_VPP(map); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_write(map, CMD(0x30), adr); ++ ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), ++ len, &retlen_dregs, buf); + +- timeo = jiffies + (HZ*20); ++ *retlen += retlen_dregs; ++ return ret; ++ } + +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. ++ return 0; ++} ++ ++ ++/* ++ * Handle devices with one erase region, that only implement ++ * the chip erase command. + */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ unsigned long timeo = jiffies + HZ; ++ unsigned long int adr; ++ DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ adr = cfi->addr_unlock1; + +- /* an initial short sleep */ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ return ret; ++ } ++ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, chip->start ); ++ ++ ENABLE_VPP(map); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; ++ ++ cfi_spin_unlock(chip->mutex); ++ msleep(chip->erase_time/2); + cfi_spin_lock(chip->mutex); + ++ timeo = jiffies + (HZ*20); ++ ++ for (;;) { + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk(KERN_DEBUG "erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ if (chip_ready(map, adr)) ++ goto op_done; ++ ++ if (time_after(jiffies, timeo)) ++ break; + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- +- cfi_udelay(1); +- ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- } +- if( (status & dq6) != (oldstatus & dq6) ) +- { +- /* The erasing didn't stop?? */ +- if( ( status & dq5 ) == dq5 ) +- { +- /* When DQ5 raises, we must check once again if DQ6 is toggling. +- If not, the erase has been completed OK. If not, reset chip. */ +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); + +- if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) ) +- { +- printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" ); +- } +- else +- { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" ); +- } +- } +- else +- { +- printk( "Waiting for erase to complete timed out in do_erase_oneblock."); ++ printk(KERN_WARNING "MTD %s(): software timeout\n", ++ __func__ ); + +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- return -EIO; +- } +- } ++ /* reset on all failures. */ ++ map_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ + +- DISABLE_VPP(map); ++ ret = -EIO; ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } + +-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++ ++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) + { +- struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; +- int chipnum, ret = 0; +- int i, first; +- struct mtd_erase_region_info *regions = mtd->eraseregions; +- +- if (instr->addr > mtd->size) +- return -EINVAL; +- +- if ((instr->len + instr->addr) > mtd->size) +- return -EINVAL; +- +- /* Check that both start and end of the requested erase are +- * aligned with the erasesize at the appropriate addresses. +- */ ++ unsigned long timeo = jiffies + HZ; ++ DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; + +- i = 0; ++ adr += chip->start; + +- /* Skip all erase regions which are ended before the start of +- the requested erase. Actually, to save on the calculations, +- we skip to the first erase region which starts after the +- start of the requested erase, and then go back one. +- */ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) +- i++; +- i--; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, adr ); + +- /* OK, now i is pointing at the erase region in which this +- erase request starts. Check the start of the requested +- erase range is aligned with the erase size which is in +- effect here. +- */ ++ ENABLE_VPP(map); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); ++ map_write(map, CMD(0x30), adr); + +- if (instr->addr & (regions[i].erasesize-1)) +- return -EINVAL; ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; + +- /* Remember the erase region we start on */ +- first = i; ++ cfi_spin_unlock(chip->mutex); ++ msleep(chip->erase_time/2); ++ cfi_spin_lock(chip->mutex); + +- /* Next, check that the end of the requested erase is aligned +- * with the erase region at that address. +- */ ++ timeo = jiffies + (HZ*20); + +- while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) +- i++; ++ for (;;) { ++ if (chip->state != FL_ERASING) { ++ /* Someone's suspended the erase. Sleep */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ cfi_spin_lock(chip->mutex); ++ continue; ++ } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } + +- /* As before, drop back one to point at the region in which +- the address actually falls +- */ +- i--; ++ if (chip_ready(map, adr)) ++ goto op_done; + +- if ((instr->addr + instr->len) & (regions[i].erasesize-1)) +- return -EINVAL; ++ if (time_after(jiffies, timeo)) ++ break; + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); +- len = instr->len; ++ /* Latency issues. Drop the lock, wait a while and retry */ ++ cfi_spin_unlock(chip->mutex); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); ++ cfi_spin_lock(chip->mutex); ++ } + +- i=first; ++ printk(KERN_WARNING "MTD %s(): software timeout\n", ++ __func__ ); + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); ++ /* reset on all failures. */ ++ map_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ + +- if (ret) ++ ret = -EIO; ++ op_done: ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); + return ret; +- +- adr += regions[i].erasesize; +- len -= regions[i].erasesize; +- +- if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) +- i++; +- +- if (adr >> cfi->chipshift) { +- adr = 0; +- chipnum++; +- +- if (chipnum >= cfi->numchips) +- break; +- } +- } +- +- instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); +- +- return 0; + } + +-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) +-{ +- struct map_info *map = mtd->priv; +- struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; +- int chipnum, ret = 0; + +- if (instr->addr & (mtd->erasesize - 1)) +- return -EINVAL; +- +- if (instr->len & (mtd->erasesize -1)) +- return -EINVAL; +- +- if ((instr->len + instr->addr) > mtd->size) +- return -EINVAL; ++int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ unsigned long ofs, len; ++ int ret; + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); ++ ofs = instr->addr; + len = instr->len; + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); +- ++ ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + if (ret) + return ret; + +- adr += mtd->erasesize; +- len -= mtd->erasesize; +- +- if (adr >> cfi->chipshift) { +- adr = 0; +- chipnum++; +- +- if (chipnum >= cfi->numchips) +- break; +- } +- } +- + instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } + ++ + static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) + { + struct map_info *map = mtd->priv; +@@ -1135,12 +1336,12 @@ + return ret; + + instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } + ++ + static void cfi_amdstd_sync (struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1254,6 +1455,7 @@ + return ret; + } + ++ + static void cfi_amdstd_resume(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1269,7 +1471,7 @@ + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = FL_READY; +- cfi_write(map, CMD(0xF0), chip->start); ++ map_write(map, CMD(0xF0), chip->start); + wake_up(&chip->wq); + } + else +@@ -1291,21 +1493,23 @@ + + static char im_name[]="cfi_cmdset_0002"; + +-int __init cfi_amdstd_init(void) ++ ++static int __init cfi_amdstd_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); + return 0; + } + ++ + static void __exit cfi_amdstd_exit(void) + { + inter_module_unregister(im_name); + } + ++ + module_init(cfi_amdstd_init); + module_exit(cfi_amdstd_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Crossnet Co. et al."); + MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); +- +--- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0020.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0020.c +@@ -4,6 +4,7 @@ + * + * (C) 2000 Red Hat. GPL'd + * ++ * $Id$ + * + * 10/10/2000 Nicolas Pitre + * - completely revamped method functions so they are aware and +@@ -17,10 +18,12 @@ + * - added a writev function + */ + ++#include + #include + #include + #include + #include ++#include + #include + #include + +@@ -30,12 +33,13 @@ + #include + #include + #include ++#include + #include + + + static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +-static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs, ++static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); + static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); + static void cfi_staa_sync (struct mtd_info *); +@@ -51,10 +55,10 @@ + static struct mtd_info *cfi_staa_setup (struct map_info *); + + static struct mtd_chip_driver cfi_staa_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_staa_destroy, +- name: "cfi_cmdset_0020", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_staa_destroy, ++ .name = "cfi_cmdset_0020", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -113,7 +117,6 @@ + { + struct cfi_private *cfi = map->fldrv_priv; + int i; +- __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode) { + /* +@@ -123,35 +126,10 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; +- +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); +- if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); +- return NULL; +- } + +- /* Read in the Extended Query Table */ +- for (i=0; iMajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { +- printk(KERN_WARNING " Unknown staa Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); ++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); ++ if (!extp) + return NULL; +- } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); +@@ -172,11 +150,6 @@ + cfi->chips[i].erase_time = 1024; + } + +- map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; +- +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_staa_setup(map); + } + +@@ -208,6 +181,7 @@ + if (!mtd->eraseregions) { + printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -232,6 +206,7 @@ + printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + kfree(mtd->eraseregions); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -256,7 +231,7 @@ + mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */ + mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ + map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + mtd->name = map->name; + return mtd; + } +@@ -264,7 +239,7 @@ + + static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { +- __u32 status, status_OK; ++ map_word status, status_OK; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + int suspended = 0; +@@ -274,7 +249,7 @@ + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ +- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ cmd_addr = adr & ~(map_bankwidth(map)-1); + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); +@@ -288,33 +263,33 @@ + */ + switch (chip->state) { + case FL_ERASING: +- if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) ++ if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) + goto sleep; /* We don't support erase suspend */ + +- cfi_write (map, CMD(0xb0), cmd_addr); ++ map_write (map, CMD(0xb0), cmd_addr); + /* If the flash has finished erasing, then 'erase suspend' + * appears to make some (28F320) flash devices switch to + * 'read' mode. Make sure that we switch to 'read status' + * mode so we get the right data. --rmk + */ +- cfi_write(map, CMD(0x70), cmd_addr); ++ map_write(map, CMD(0x70), cmd_addr); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; + // printk("Erase suspending at 0x%lx\n", cmd_addr); + for (;;) { +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_addr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + if (time_after(jiffies, timeo)) { + /* Urgh */ +- cfi_write(map, CMD(0xd0), cmd_addr); ++ map_write(map, CMD(0xd0), cmd_addr); + /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), cmd_addr); ++ map_write(map, CMD(0x70), cmd_addr); + chip->state = FL_ERASING; + spin_unlock_bh(chip->mutex); + printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); ++ "suspended: status = 0x%lx\n", status.x[0]); + return -EIO; + } + +@@ -324,7 +299,7 @@ + } + + suspended = 1; +- cfi_write(map, CMD(0xff), cmd_addr); ++ map_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; + break; + +@@ -338,13 +313,13 @@ + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); ++ map_write(map, CMD(0x70), cmd_addr); + chip->state = FL_STATUS; + + case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); ++ status = map_read(map, cmd_addr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) { ++ map_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; + break; + } +@@ -352,7 +327,7 @@ + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status); ++ printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); + return -EIO; + } + +@@ -374,7 +349,7 @@ + goto retry; + } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + if (suspended) { + chip->state = chip->oldstate; +@@ -387,8 +362,8 @@ + sending the 0x70 (Read Status) command to an erasing + chip and expecting it to be ignored, that's what we + do. */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- cfi_write(map, CMD(0x70), cmd_addr); ++ map_write(map, CMD(0xd0), cmd_addr); ++ map_write(map, CMD(0x70), cmd_addr); + } + + wake_up(&chip->wq); +@@ -439,16 +414,16 @@ + unsigned long adr, const u_char *buf, int len) + { + struct cfi_private *cfi = map->fldrv_priv; +- __u32 status, status_OK; ++ map_word status, status_OK; + unsigned long cmd_adr, timeo; + DECLARE_WAITQUEUE(wait, current); + int wbufsize, z; + + /* M58LW064A requires bus alignment for buffer wriets -- saw */ +- if (adr & (CFIDEV_BUSWIDTH-1)) ++ if (adr & (map_bankwidth(map)-1)) + return -EINVAL; + +- wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; + cmd_adr = adr & ~(wbufsize-1); + +@@ -474,21 +449,21 @@ + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_adr); ++ map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + #ifdef DEBUG_CFI_FEATURES +- printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr)); ++ printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); + #endif + + case FL_STATUS: +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %x\n", +- status, cfi_read(map, cmd_adr)); ++ printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", ++ status.x[0], map_read(map, cmd_adr).x[0]); + return -EIO; + } + +@@ -510,13 +485,13 @@ + } + + ENABLE_VPP(map); +- cfi_write(map, CMD(0xe8), cmd_adr); ++ map_write(map, CMD(0xe8), cmd_adr); + chip->state = FL_WRITING_TO_BUFFER; + + z = 0; + for (;;) { +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + spin_unlock_bh(chip->mutex); +@@ -526,32 +501,26 @@ + if (++z > 100) { + /* Argh. Not ready for write to buffer */ + DISABLE_VPP(map); +- cfi_write(map, CMD(0x70), cmd_adr); ++ map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); +- printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status); ++ printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); + return -EIO; + } + } + + /* Write length of data to come */ +- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); ++ map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr ); + + /* Write data */ +- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { +- if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); +- } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); +- } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); +- } else { +- DISABLE_VPP(map); +- return -EINVAL; +- } ++ for (z = 0; z < len; ++ z += map_bankwidth(map), buf += map_bankwidth(map)) { ++ map_word d; ++ d = map_word_load(map, buf); ++ map_write(map, d, adr+z); + } + /* GO GO GO */ +- cfi_write(map, CMD(0xd0), cmd_adr); ++ map_write(map, CMD(0xd0), cmd_adr); + chip->state = FL_WRITING; + + spin_unlock_bh(chip->mutex); +@@ -573,16 +542,16 @@ + continue; + } + +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, cmd_adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + /* clear status */ +- cfi_write(map, CMD(0x50), cmd_adr); ++ map_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); +@@ -609,18 +578,17 @@ + chip->state = FL_STATUS; + + /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ +- if ((status & CMD(0x02)) || (status & CMD(0x08)) || +- (status & CMD(0x10)) || (status & CMD(0x20))) { ++ if (map_word_bitsset(map, status, CMD(0x3a))) { + #ifdef DEBUG_CFI_FEATURES +- printk("%s: 2 status[%x]\n", __FUNCTION__, status); ++ printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); + #endif + /* clear status */ +- cfi_write(map, CMD(0x50), cmd_adr); ++ map_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +- return (status & CMD(0x02)) ? -EROFS : -EIO; ++ return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; + } + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +@@ -633,7 +601,7 @@ + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + int ret = 0; + int chipnum; + unsigned long ofs; +@@ -646,7 +614,7 @@ + ofs = to - (chipnum << cfi->chipshift); + + #ifdef DEBUG_CFI_FEATURES +- printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH); ++ printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); + printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); + printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); + #endif +@@ -689,7 +657,7 @@ + #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) + #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) + static int +-cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs, ++cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) + { + unsigned long i; +@@ -758,7 +726,7 @@ + static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + { + struct cfi_private *cfi = map->fldrv_priv; +- __u32 status, status_OK; ++ map_word status, status_OK; + unsigned long timeo; + int retries = 3; + DECLARE_WAITQUEUE(wait, current); +@@ -778,12 +746,12 @@ + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* Urgh. Chip not yet ready to talk to us. */ +@@ -812,15 +780,15 @@ + + ENABLE_VPP(map); + /* Clear the status register first */ +- cfi_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x50), adr); + + /* Now erase */ +- cfi_write(map, CMD(0x20), adr); +- cfi_write(map, CMD(0xD0), adr); ++ map_write(map, CMD(0x20), adr); ++ map_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; + + spin_unlock_bh(chip->mutex); +- schedule_timeout(HZ); ++ msleep(1000); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ +@@ -840,15 +808,15 @@ + continue; + } + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); ++ printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; +@@ -864,43 +832,46 @@ + ret = 0; + + /* We've broken this before. It doesn't hurt to be safe */ +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- status = cfi_read(map, adr); ++ status = map_read(map, adr); + + /* check for lock bit */ +- if (status & CMD(0x3a)) { +- unsigned char chipstatus = status; +- if (status != CMD(status & 0xff)) { +- int i; +- for (i = 1; i> (cfi->device_type * 8); ++ if (map_word_bitsset(map, status, CMD(0x3a))) { ++ unsigned char chipstatus = status.x[0]; ++ if (!map_word_equal(map, status, CMD(chipstatus))) { ++ int i, w; ++ for (w=0; w> (cfi->device_type * 8); + } +- printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); ++ } ++ printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", ++ status.x[0], chipstatus); + } + /* Reset the error bits */ +- cfi_write(map, CMD(0x50), adr); +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x50), adr); ++ map_write(map, CMD(0x70), adr); + + if ((chipstatus & 0x30) == 0x30) { +- printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); ++ printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); + ret = -EIO; + } else if (chipstatus & 0x02) { + /* Protection bit set */ + ret = -EROFS; + } else if (chipstatus & 0x8) { + /* Voltage */ +- printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); ++ printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); + ret = -EIO; + } else if (chipstatus & 0x20) { + if (retries--) { +- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); ++ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); + timeo = jiffies + HZ; + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + goto retry; + } +- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); ++ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); + ret = -EIO; + } + } +@@ -995,8 +966,7 @@ + } + + instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } +@@ -1061,7 +1031,7 @@ + static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + { + struct cfi_private *cfi = map->fldrv_priv; +- __u32 status, status_OK; ++ map_word status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + +@@ -1079,12 +1049,12 @@ + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* Urgh. Chip not yet ready to talk to us. */ +@@ -1112,12 +1082,12 @@ + } + + ENABLE_VPP(map); +- cfi_write(map, CMD(0x60), adr); +- cfi_write(map, CMD(0x01), adr); ++ map_write(map, CMD(0x60), adr); ++ map_write(map, CMD(0x01), adr); + chip->state = FL_LOCKING; + + spin_unlock_bh(chip->mutex); +- schedule_timeout(HZ); ++ msleep(1000); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ +@@ -1126,15 +1096,15 @@ + timeo = jiffies + (HZ*2); + for (;;) { + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); ++ printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; +@@ -1210,7 +1180,7 @@ + static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + { + struct cfi_private *cfi = map->fldrv_priv; +- __u32 status, status_OK; ++ map_word status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + +@@ -1228,12 +1198,12 @@ + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* Urgh. Chip not yet ready to talk to us. */ +@@ -1261,12 +1231,12 @@ + } + + ENABLE_VPP(map); +- cfi_write(map, CMD(0x60), adr); +- cfi_write(map, CMD(0xD0), adr); ++ map_write(map, CMD(0x60), adr); ++ map_write(map, CMD(0xD0), adr); + chip->state = FL_UNLOCKING; + + spin_unlock_bh(chip->mutex); +- schedule_timeout(HZ); ++ msleep(1000); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ +@@ -1275,15 +1245,15 @@ + timeo = jiffies + (HZ*2); + for (;;) { + +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) ++ status = map_read(map, adr); ++ if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { +- cfi_write(map, CMD(0x70), adr); ++ map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; +- printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); ++ printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; +@@ -1412,7 +1382,7 @@ + + /* Go to known state. Chip may have been power cycled */ + if (chip->state == FL_PM_SUSPENDED) { +- cfi_write(map, CMD(0xFF), 0); ++ map_write(map, CMD(0xFF), 0); + chip->state = FL_READY; + wake_up(&chip->wq); + } +@@ -1429,23 +1399,20 @@ + kfree(cfi); + } + +-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +-#define cfi_staa_init init_module +-#define cfi_staa_exit cleanup_module +-#endif +- + static char im_name[]="cfi_cmdset_0020"; + +-mod_init_t cfi_staa_init(void) ++static int __init cfi_staa_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); + return 0; + } + +-mod_exit_t cfi_staa_exit(void) ++static void __exit cfi_staa_exit(void) + { + inter_module_unregister(im_name); + } + + module_init(cfi_staa_init); + module_exit(cfi_staa_exit); ++ ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/chips/cfi_probe.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/cfi_probe.c +@@ -1,19 +1,21 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id$ ++ $Id$ + */ + + #include + #include + #include + #include ++#include + #include + #include + #include + #include + #include + ++#include + #include + #include + #include +@@ -25,30 +27,80 @@ + #endif + + static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); + + struct mtd_info *cfi_probe(struct map_info *map); + ++#ifdef CONFIG_MTD_XIP ++ ++/* only needed for short periods, so this is rather simple */ ++#define xip_disable() local_irq_disable() ++ ++#define xip_allowed(base, map) \ ++do { \ ++ (void) map_read(map, base); \ ++ asm volatile (".rep 8; nop; .endr"); \ ++ local_irq_enable(); \ ++} while (0) ++ ++#define xip_enable(base, map, cfi) \ ++do { \ ++ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ ++ xip_allowed(base, map); \ ++} while (0) ++ ++#define xip_disable_qry(base, map, cfi) \ ++do { \ ++ xip_disable(); \ ++ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ ++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ ++} while (0) ++ ++#else ++ ++#define xip_disable() do { } while (0) ++#define xip_allowed(base, map) do { } while (0) ++#define xip_enable(base, map, cfi) do { } while (0) ++#define xip_disable_qry(base, map, cfi) do { } while (0) ++ ++#endif ++ + /* check for QRY. + in: interleave,type,mode + ret: table index, <0 for error + */ +-static inline int qry_present(struct map_info *map, __u32 base, ++static int __xipram qry_present(struct map_info *map, __u32 base, + struct cfi_private *cfi) + { + int osf = cfi->interleave * cfi->device_type; // scale factor ++ map_word val[3]; ++ map_word qry[3]; + +- if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) && +- cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) && +- cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi)) +- return 1; // ok ! ++ qry[0] = cfi_build_cmd('Q', map, cfi); ++ qry[1] = cfi_build_cmd('R', map, cfi); ++ qry[2] = cfi_build_cmd('Y', map, cfi); + +- return 0; // nothing found ++ val[0] = map_read(map, base + osf*0x10); ++ val[1] = map_read(map, base + osf*0x11); ++ val[2] = map_read(map, base + osf*0x12); ++ ++ if (!map_word_equal(map, qry[0], val[0])) ++ return 0; ++ ++ if (!map_word_equal(map, qry[1], val[1])) ++ return 0; ++ ++ if (!map_word_equal(map, qry[2], val[2])) ++ return 0; ++ ++ return 1; // "QRY" found + } + +-static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, ++ unsigned long *chip_map, struct cfi_private *cfi) + { + int i; + +@@ -64,15 +116,16 @@ + (unsigned long)base + 0x55, map->size -1); + return 0; + } +- cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + +- /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ xip_disable(); ++ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); +- + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + +- if (!qry_present(map,base,cfi)) ++ if (!qry_present(map,base,cfi)) { ++ xip_enable(base, map, cfi); + return 0; ++ } + + if (!cfi->numchips) { + /* This is the first time we're called. Set up the CFI +@@ -81,20 +134,26 @@ + } + + /* Check each previous chip to see if it's an alias */ +- for (i=0; inumchips; i++) { ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ /* Skip location; no valid chip at this address */ ++ continue; ++ } ++ start = i << cfi->chipshift; + /* This chip should be in read mode if it's one + we've already touched. */ +- if (qry_present(map,chips[i].start,cfi)) { ++ if (qry_present(map, start, cfi)) { + /* Eep. This chip also had the QRY marker. + * Is it an alias for the new one? */ +- cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); +- /* some devices don't respond to 0xF0, so send 0xFF to be sure */ +- cfi_send_gen_cmd(0xFF, 0, chips[i].start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); + + /* If the QRY marker goes away, it's an alias */ +- if (!qry_present(map, chips[i].start, cfi)) { ++ if (!qry_present(map, start, cfi)) { ++ xip_allowed(base, map); + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + /* Yes, it's actually got QRY for data. Most +@@ -102,11 +161,12 @@ + * too and if it's the same, assume it's an alias. */ + /* FIXME: Use other modes to do a proper check */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); +- /* some devices don't respond to 0xF0, so send 0xFF to be sure */ +- cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); ++ + if (qry_present(map, base, cfi)) { ++ xip_allowed(base, map); + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -114,30 +174,22 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); +- +- /* some devices don't respond to 0xF0, so send 0xFF to be sure */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ xip_allowed(base, map); + +- +- printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", ++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", + map->name, cfi->interleave, cfi->device_type*8, base, +- map->buswidth*8); ++ map->bankwidth*8); + + return 1; + } + +-static int cfi_chip_setup(struct map_info *map, ++static int __xipram cfi_chip_setup(struct map_info *map, + struct cfi_private *cfi) + { + int ofs_factor = cfi->interleave*cfi->device_type; +@@ -145,6 +197,7 @@ + int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); + int i; + ++ xip_enable(base, map, cfi); + #ifdef DEBUG_CFI + printk("Number of erase regions: %d\n", num_erase_regions); + #endif +@@ -160,12 +213,31 @@ + memset(cfi->cfiq,0,sizeof(struct cfi_ident)); + + cfi->cfi_mode = CFI_MODE_CFI; +- cfi->fast_prog=1; /* CFI supports fast programming */ + + /* Read the CFI info structure */ +- for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { ++ xip_disable_qry(base, map, cfi); ++ for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) + ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); +- } ++ ++ /* Note we put the device back into Read Mode BEFORE going into Auto ++ * Select Mode, as some devices support nesting of modes, others ++ * don't. This way should always work. ++ * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and ++ * so should be treated as nops or illegal (and so put the device ++ * back into Read Mode, which is a nop in this case). ++ */ ++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi->mfr = cfi_read_query(map, base); ++ cfi->id = cfi_read_query(map, base + ofs_factor); ++ ++ /* Put it back into Read Mode */ ++ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); ++ /* ... even if it's an Intel chip */ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ xip_allowed(base, map); + + /* Do any necessary byteswapping */ + cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); +@@ -176,20 +248,6 @@ + cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc); + cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize); + +- /* +- * ST screwed up the CFI interface for buffer writes on their parts, +- * so this needs to be fixed up by hand here. +- * +- * A possible enhancment is that instead of just reverting back +- * to word write (as this does), we could use the ST specific double +- * word write instead. +- */ +- +- if (cfi_read_query(map,base) == 0x20){ +- cfi->cfiq->BufWriteTimeoutTyp = 0; +- cfi->cfiq->BufWriteTimeoutMax = 0; +- } +- + #ifdef DEBUG_CFI + /* Dump the information therein */ + print_cfi_ident(cfi->cfiq); +@@ -204,11 +262,10 @@ + (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1); + #endif + } +- /* Put it back into Read Mode */ +- cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + +- /* some devices don't respond to 0xF0, so send 0xFF to be sure */ +- cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", ++ map->name, cfi->interleave, cfi->device_type*8, base, ++ map->bankwidth*8); + + return 1; + } +@@ -232,12 +289,27 @@ + case P_ID_AMD_EXT: + return "AMD/Fujitsu Extended"; + ++ case P_ID_WINBOND: ++ return "Winbond Standard"; ++ ++ case P_ID_ST_ADV: ++ return "ST Advanced"; ++ + case P_ID_MITSUBISHI_STD: + return "Mitsubishi Standard"; + + case P_ID_MITSUBISHI_EXT: + return "Mitsubishi Extended"; + ++ case P_ID_SST_PAGE: ++ return "SST Page Write"; ++ ++ case P_ID_INTEL_PERFORMANCE: ++ return "Intel Performance Code"; ++ ++ case P_ID_INTEL_DATA: ++ return "Intel Data"; ++ + case P_ID_RESERVED: + return "Not Allowed / Reserved for Future Use"; + +@@ -268,11 +340,11 @@ + printk("No Alternate Algorithm Table\n"); + + +- printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); +- printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); ++ printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); ++ printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + if (cfip->VppMin) { +- printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); +- printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); ++ printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); ++ printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + } + else + printk("No Vpp line\n"); +@@ -315,6 +387,10 @@ + printk(" - x32-only asynchronous interface\n"); + break; + ++ case 4: ++ printk(" - supports x16 and x32 via Word# with asynchronous interface\n"); ++ break; ++ + case 65535: + printk(" - Not Allowed / Reserved\n"); + break; +@@ -331,8 +407,8 @@ + #endif /* DEBUG_CFI */ + + static struct chip_probe cfi_chip_probe = { +- name: "CFI", +- probe_chip: cfi_probe_chip ++ .name = "CFI", ++ .probe_chip = cfi_probe_chip + }; + + struct mtd_info *cfi_probe(struct map_info *map) +@@ -345,9 +421,9 @@ + } + + static struct mtd_chip_driver cfi_chipdrv = { +- probe: cfi_probe, +- name: "cfi_probe", +- module: THIS_MODULE ++ .probe = cfi_probe, ++ .name = "cfi_probe", ++ .module = THIS_MODULE + }; + + int __init cfi_probe_init(void) +--- /dev/null ++++ linux-2.4.21/drivers/mtd/chips/cfi_util.c +@@ -0,0 +1,196 @@ ++/* ++ * Common Flash Interface support: ++ * Generic utility functions not dependant on command set ++ * ++ * Copyright (C) 2002 Red Hat ++ * Copyright (C) 2003 STMicroelectronics Limited ++ * ++ * This code is covered by the GPL. ++ * ++ * $Id$ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct cfi_extquery * ++__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ __u32 base = 0; // cfi->chips[0].start; ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ int i; ++ struct cfi_extquery *extp = NULL; ++ ++ printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); ++ if (!adr) ++ goto out; ++ ++ extp = kmalloc(size, GFP_KERNEL); ++ if (!extp) { ++ printk(KERN_ERR "Failed to allocate memory\n"); ++ goto out; ++ } ++ ++#ifdef CONFIG_MTD_XIP ++ local_irq_disable(); ++#endif ++ ++ /* Switch it into Query Mode */ ++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ ++ /* Read in the Extended Query Table */ ++ for (i=0; idevice_type, NULL); ++ cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); ++ ++#ifdef CONFIG_MTD_XIP ++ (void) map_read(map, base); ++ asm volatile (".rep 8; nop; .endr"); ++ local_irq_enable(); ++#endif ++ ++ if (extp->MajorVersion != '1' || ++ (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { ++ printk(KERN_WARNING " Unknown %s Extended Query " ++ "version %c.%c.\n", name, extp->MajorVersion, ++ extp->MinorVersion); ++ kfree(extp); ++ extp = NULL; ++ } ++ ++ out: return extp; ++} ++ ++EXPORT_SYMBOL(cfi_read_pri); ++ ++void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_fixup *f; ++ ++ for (f=fixups; f->fixup; f++) { ++ if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && ++ ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { ++ f->fixup(mtd, f->param); ++ } ++ } ++} ++ ++EXPORT_SYMBOL(cfi_fixup); ++ ++int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, ++ loff_t ofs, size_t len, void *thunk) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ unsigned long adr; ++ int chipnum, ret = 0; ++ int i, first; ++ struct mtd_erase_region_info *regions = mtd->eraseregions; ++ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ ++ if ((len + ofs) > mtd->size) ++ return -EINVAL; ++ ++ /* Check that both start and end of the requested erase are ++ * aligned with the erasesize at the appropriate addresses. ++ */ ++ ++ i = 0; ++ ++ /* Skip all erase regions which are ended before the start of ++ the requested erase. Actually, to save on the calculations, ++ we skip to the first erase region which starts after the ++ start of the requested erase, and then go back one. ++ */ ++ ++ while (i < mtd->numeraseregions && ofs >= regions[i].offset) ++ i++; ++ i--; ++ ++ /* OK, now i is pointing at the erase region in which this ++ erase request starts. Check the start of the requested ++ erase range is aligned with the erase size which is in ++ effect here. ++ */ ++ ++ if (ofs & (regions[i].erasesize-1)) ++ return -EINVAL; ++ ++ /* Remember the erase region we start on */ ++ first = i; ++ ++ /* Next, check that the end of the requested erase is aligned ++ * with the erase region at that address. ++ */ ++ ++ while (inumeraseregions && (ofs + len) >= regions[i].offset) ++ i++; ++ ++ /* As before, drop back one to point at the region in which ++ the address actually falls ++ */ ++ i--; ++ ++ if ((ofs + len) & (regions[i].erasesize-1)) ++ return -EINVAL; ++ ++ chipnum = ofs >> cfi->chipshift; ++ adr = ofs - (chipnum << cfi->chipshift); ++ ++ i=first; ++ ++ while(len) { ++ int size = regions[i].erasesize; ++ ++ ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); ++ ++ if (ret) ++ return ret; ++ ++ adr += size; ++ ofs += size; ++ len -= size; ++ ++ if (ofs == regions[i].offset + size * regions[i].numblocks) ++ i++; ++ ++ if (adr >> cfi->chipshift) { ++ adr = 0; ++ chipnum++; ++ ++ if (chipnum >= cfi->numchips) ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(cfi_varsize_frob); ++ ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/chips/chipreg.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/chipreg.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Registration for chip drivers + * +@@ -7,12 +7,15 @@ + + #include + #include ++#include + #include + #include +-#include ++#include + #include ++#include ++#include + +-spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; ++static DEFINE_SPINLOCK(chip_drvs_lock); + static LIST_HEAD(chip_drvs_list); + + void register_mtd_chip_driver(struct mtd_chip_driver *drv) +@@ -44,10 +47,8 @@ + break; + } + } +- if (ret && !try_inc_mod_count(ret->module)) { +- /* Eep. Failed. */ ++ if (ret && !try_module_get(ret->module)) + ret = NULL; +- } + + spin_unlock(&chip_drvs_lock); + +@@ -64,32 +65,46 @@ + + drv = get_mtd_chip_driver(name); + +- if (!drv && !request_module(name)) ++ if (!drv && !request_module("%s", name)) + drv = get_mtd_chip_driver(name); + + if (!drv) + return NULL; + + ret = drv->probe(map); +-#ifdef CONFIG_MODULES ++ + /* We decrease the use count here. It may have been a + probe-only module, which is no longer required from this + point, having given us a handle on (and increased the use + count of) the actual driver code. + */ +- if(drv->module) +- __MOD_DEC_USE_COUNT(drv->module); +-#endif ++ module_put(drv->module); + + if (ret) + return ret; + + return NULL; + } ++/* ++ * Destroy an MTD device which was created for a map device. ++ * Make sure the MTD device is already unregistered before calling this ++ */ ++void map_destroy(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ ++ if (map->fldrv->destroy) ++ map->fldrv->destroy(mtd); ++ ++ module_put(map->fldrv->module); ++ ++ kfree(mtd); ++} + + EXPORT_SYMBOL(register_mtd_chip_driver); + EXPORT_SYMBOL(unregister_mtd_chip_driver); + EXPORT_SYMBOL(do_map_probe); ++EXPORT_SYMBOL(map_destroy); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("David Woodhouse "); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/chips/fwh_lock.h +@@ -0,0 +1,107 @@ ++#ifndef FWH_LOCK_H ++#define FWH_LOCK_H ++ ++ ++enum fwh_lock_state { ++ FWH_UNLOCKED = 0, ++ FWH_DENY_WRITE = 1, ++ FWH_IMMUTABLE = 2, ++ FWH_DENY_READ = 4, ++}; ++ ++struct fwh_xxlock_thunk { ++ enum fwh_lock_state val; ++ flstate_t state; ++}; ++ ++ ++#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) ++#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) ++ ++/* ++ * This locking/unlock is specific to firmware hub parts. Only one ++ * is known that supports the Intel command set. Firmware ++ * hub parts cannot be interleaved as they are on the LPC bus ++ * so this code has not been tested with interleaved chips, ++ * and will likely fail in that context. ++ */ ++static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr, int len, void *thunk) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; ++ int ret; ++ ++ /* Refuse the operation if the we cannot look behind the chip */ ++ if (chip->start < 0x400000) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): chip->start: %lx wanted >= 0x400000\n", ++ __func__, chip->start ); ++ return -EIO; ++ } ++ /* ++ * lock block registers: ++ * - on 64k boundariesand ++ * - bit 1 set high ++ * - block lock registers are 4MiB lower - overflow subtract (danger) ++ * ++ * The address manipulation is first done on the logical address ++ * which is 0 at the start of the chip, and then the offset of ++ * the individual chip is addted to it. Any other order a weird ++ * map offset could cause problems. ++ */ ++ adr = (adr & ~0xffffUL) | 0x2; ++ adr += chip->start - 0x400000; ++ ++ /* ++ * This is easy because these are writes to registers and not writes ++ * to flash memory - that means that we don't have to check status ++ * and timeout. ++ */ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ chip->state = xxlt->state; ++ map_write(map, CMD(xxlt->val), adr); ++ ++ /* Done and happy. */ ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); ++ return 0; ++} ++ ++ ++static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ int ret; ++ ++ ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, ++ (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); ++ ++ return ret; ++} ++ ++ ++static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ int ret; ++ ++ ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, ++ (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); ++ ++ return ret; ++} ++ ++static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) ++{ ++ printk(KERN_NOTICE "using fwh lock/unlock method\n"); ++ /* Setup for the chips with the fwh lock method */ ++ mtd->lock = fwh_lock_varsize; ++ mtd->unlock = fwh_unlock_varsize; ++} ++#endif /* FWH_LOCK_H */ +--- linux-2.4.21/drivers/mtd/chips/gen_probe.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/gen_probe.c +@@ -1,11 +1,13 @@ + /* + * Routines common to all CFI-type probes. +- * (C) 2001, 2001 Red Hat, Inc. ++ * (C) 2001-2003 Red Hat, Inc. + * GPL'd +- * $Id$ ++ * $Id$ + */ + + #include ++#include ++#include + #include + #include + #include +@@ -48,13 +50,13 @@ + EXPORT_SYMBOL(mtd_do_chip_probe); + + +-struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) ++static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) + { +- unsigned long base=0; + struct cfi_private cfi; + struct cfi_private *retcfi; +- struct flchip chip[MAX_CFI_CHIPS]; +- int i; ++ unsigned long *chip_map; ++ int i, j, mapsize; ++ int max_chips; + + memset(&cfi, 0, sizeof(cfi)); + +@@ -62,7 +64,7 @@ + interleave and device type, etc. */ + if (!genprobe_new_chip(map, cp, &cfi)) { + /* The probe didn't like it */ +- printk(KERN_WARNING "%s: Found no %s device at location zero\n", ++ printk(KERN_DEBUG "%s: Found no %s device at location zero\n", + cp->name, map->name); + return NULL; + } +@@ -77,46 +79,47 @@ + return NULL; + } + #endif +- chip[0].start = 0; +- chip[0].state = FL_READY; + cfi.chipshift = cfi.cfiq->DevSize; + +- switch(cfi.interleave) { +-#ifdef CFIDEV_INTERLEAVE_1 +- case 1: +- break; +-#endif +-#ifdef CFIDEV_INTERLEAVE_2 +- case 2: ++ if (cfi_interleave_is_1(&cfi)) { ++ ; ++ } else if (cfi_interleave_is_2(&cfi)) { + cfi.chipshift++; +- break; +-#endif +-#ifdef CFIDEV_INTERLEAVE_4 +- case 4: +- cfi.chipshift+=2; +- break; +-#endif +- default: ++ } else if (cfi_interleave_is_4((&cfi))) { ++ cfi.chipshift += 2; ++ } else if (cfi_interleave_is_8(&cfi)) { ++ cfi.chipshift += 3; ++ } else { + BUG(); + } + + cfi.numchips = 1; + + /* ++ * Allocate memory for bitmap of valid chips. ++ * Align bitmap storage size to full byte. ++ */ ++ max_chips = map->size >> cfi.chipshift; ++ mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0); ++ chip_map = kmalloc(mapsize, GFP_KERNEL); ++ if (!chip_map) { ++ printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); ++ kfree(cfi.cfiq); ++ return NULL; ++ } ++ memset (chip_map, 0, mapsize); ++ ++ set_bit(0, chip_map); /* Mark first chip valid */ ++ ++ /* + * Now probe for other chips, checking sensibly for aliases while + * we're at it. The new_chip probe above should have let the first + * chip in read mode. +- * +- * NOTE: Here, we're checking if there is room for another chip +- * the same size within the mapping. Therefore, +- * base + chipsize <= map->size is the correct thing to do, +- * because, base + chipsize would be the _first_ byte of the +- * next chip, not the one we're currently pondering. + */ + +- for (base = (1<size; +- base += (1<probe_chip(map, base, &chip[0], &cfi); ++ for (i = 1; i < max_chips; i++) { ++ cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); ++ } + + /* + * Now allocate the space for the structures we need to return to +@@ -128,19 +131,26 @@ + if (!retcfi) { + printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); + kfree(cfi.cfiq); ++ kfree(chip_map); + return NULL; + } + + memcpy(retcfi, &cfi, sizeof(cfi)); +- memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); ++ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); + +- /* Fix up the stuff that breaks when you move it */ +- for (i=0; i< retcfi->numchips; i++) { +- init_waitqueue_head(&retcfi->chips[i].wq); +- spin_lock_init(&retcfi->chips[i]._spinlock); +- retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock; ++ for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { ++ if(test_bit(i, chip_map)) { ++ struct flchip *pchip = &retcfi->chips[j++]; ++ ++ pchip->start = (i << cfi.chipshift); ++ pchip->state = FL_READY; ++ init_waitqueue_head(&pchip->wq); ++ spin_lock_init(&pchip->_spinlock); ++ pchip->mutex = &pchip->_spinlock; ++ } + } + ++ kfree(chip_map); + return retcfi; + } + +@@ -148,135 +158,36 @@ + static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, + struct cfi_private *cfi) + { +- switch (map->buswidth) { +-#ifdef CFIDEV_BUSWIDTH_1 +- case CFIDEV_BUSWIDTH_1: +- cfi->interleave = CFIDEV_INTERLEAVE_1; +- +- cfi->device_type = CFI_DEVICETYPE_X8; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +- +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +- break; +-#endif /* CFIDEV_BUSWITDH_1 */ +- +-#ifdef CFIDEV_BUSWIDTH_2 +- case CFIDEV_BUSWIDTH_2: +-#ifdef CFIDEV_INTERLEAVE_1 +- cfi->interleave = CFIDEV_INTERLEAVE_1; +- +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_1 */ +-#ifdef CFIDEV_INTERLEAVE_2 +- cfi->interleave = CFIDEV_INTERLEAVE_2; +- +- cfi->device_type = CFI_DEVICETYPE_X8; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +- +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_2 */ +- break; +-#endif /* CFIDEV_BUSWIDTH_2 */ +- +-#ifdef CFIDEV_BUSWIDTH_4 +- case CFIDEV_BUSWIDTH_4: +-#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE) +- cfi->interleave = CFIDEV_INTERLEAVE_1; +- +- cfi->device_type = CFI_DEVICETYPE_X32; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_1 */ +-#ifdef CFIDEV_INTERLEAVE_2 +- cfi->interleave = CFIDEV_INTERLEAVE_2; +- +-#ifdef SOMEONE_ACTUALLY_MAKES_THESE +- cfi->device_type = CFI_DEVICETYPE_X32; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +- +- cfi->device_type = CFI_DEVICETYPE_X8; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_2 */ +-#ifdef CFIDEV_INTERLEAVE_4 +- cfi->interleave = CFIDEV_INTERLEAVE_4; +- +-#ifdef SOMEONE_ACTUALLY_MAKES_THESE +- cfi->device_type = CFI_DEVICETYPE_X32; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; ++ int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ ++ int max_chips = map_bankwidth(map); /* And minimum 1 */ ++ int nr_chips, type; + +- cfi->device_type = CFI_DEVICETYPE_X8; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_4 */ +- break; +-#endif /* CFIDEV_BUSWIDTH_4 */ ++ for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { + +-#ifdef CFIDEV_BUSWIDTH_8 +- case CFIDEV_BUSWIDTH_8: +-#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE) +- cfi->interleave = CFIDEV_INTERLEAVE_2; ++ if (!cfi_interleave_supported(nr_chips)) ++ continue; + +- cfi->device_type = CFI_DEVICETYPE_X32; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_2 */ +-#ifdef CFIDEV_INTERLEAVE_4 +- cfi->interleave = CFIDEV_INTERLEAVE_4; ++ cfi->interleave = nr_chips; + +-#ifdef SOMEONE_ACTUALLY_MAKES_THESE +- cfi->device_type = CFI_DEVICETYPE_X32; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; +-#endif /* CFIDEV_INTERLEAVE_4 */ +-#ifdef CFIDEV_INTERLEAVE_8 +- cfi->interleave = CFIDEV_INTERLEAVE_8; ++ /* Minimum device size. Don't look for one 8-bit device ++ in a 16-bit bus, etc. */ ++ type = map_bankwidth(map) / nr_chips; + +- cfi->device_type = CFI_DEVICETYPE_X16; +- if (cp->probe_chip(map, 0, NULL, cfi)) +- return 1; ++ for (; type <= CFI_DEVICETYPE_X32; type<<=1) { ++ cfi->device_type = type; + +- cfi->device_type = CFI_DEVICETYPE_X8; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; +-#endif /* CFIDEV_INTERLEAVE_8 */ +- break; +-#endif /* CFIDEV_BUSWIDTH_8 */ +- +- default: +- printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth); +- return 0; ++ } + } + return 0; + } + +- + typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); + + extern cfi_cmdset_fn_t cfi_cmdset_0001; + extern cfi_cmdset_fn_t cfi_cmdset_0002; ++extern cfi_cmdset_fn_t cfi_cmdset_0020; + + static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, + int primary) +--- linux-2.4.21/drivers/mtd/chips/jedec.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/jedec.c +@@ -11,10 +11,16 @@ + * not going to guess how to send commands to them, plus I expect they will + * all speak CFI.. + * +- * $Id$ ++ * $Id$ + */ + ++#include ++#include ++#include + #include ++#include ++#include ++#include + + static struct mtd_info *jedec_probe(struct map_info *); + static int jedec_probe8(struct map_info *map,unsigned long base, +@@ -33,14 +39,51 @@ + + /* Listing of parts and sizes. We need this table to learn the sector + size of the chip and the total length */ +-static const struct JEDECTable JEDEC_table[] = +- {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {}}; ++static const struct JEDECTable JEDEC_table[] = { ++ { ++ .jedec = 0x013D, ++ .name = "AMD Am29F017D", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01AD, ++ .name = "AMD Am29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01D5, ++ .name = "AMD Am29F080", ++ .size = 1*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01A4, ++ .name = "AMD Am29F040", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x20E3, ++ .name = "AMD Am29W040B", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0xC2AD, ++ .name = "Macronix MX29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { .jedec = 0x0 } ++}; + + static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id); + static void jedec_sync(struct mtd_info *mtd) {}; +@@ -54,9 +97,9 @@ + + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec", ++ .module = THIS_MODULE + }; + + /* Probe entry point */ +@@ -85,7 +128,7 @@ + { + printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + + for (Base = 0; Base < map->size; Base += my_bank_size) +@@ -98,7 +141,7 @@ + if (jedec_probe8(map,Base,priv) == 0) { + printk("did recognize jedec chip\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + } + if (map->buswidth == 2) +@@ -124,15 +167,14 @@ + { + printk("mtd: Failed. Device has incompatible mixed sector sizes\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + } + + /* Generate a part name that includes the number of different chips and + other configuration information */ + count = 1; +- strncpy(Part,map->name,sizeof(Part)-10); +- Part[sizeof(Part)-11] = 0; ++ strlcpy(Part,map->name,sizeof(Part)-10); + strcat(Part," "); + Uniq = 0; + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) +@@ -151,7 +193,7 @@ + { + printk("mtd: Internal Error, JEDEC not set\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + + if (Uniq != 0) +@@ -179,7 +221,7 @@ + if (!priv->size) { + printk("priv->size is zero\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + if (priv->size/my_bank_size) { + if (priv->size/my_bank_size == 1) { +@@ -198,7 +240,7 @@ + { + printk("mtd: Failed. Cannot handle unsymmetric banking\n"); + kfree(MTD); +- return 0; ++ return NULL; + } + } + } +@@ -209,8 +251,7 @@ + // printk("Part: '%s'\n",Part); + + memset(MTD,0,sizeof(*MTD)); +- // strncpy(MTD->name,Part,sizeof(MTD->name)); +- // MTD->name[sizeof(MTD->name)-1] = 0; ++ // strlcpy(MTD->name,Part,sizeof(MTD->name)); + MTD->name = map->name; + MTD->type = MTD_NORFLASH; + MTD->flags = MTD_CAP_NORFLASH; +@@ -229,7 +270,7 @@ + MTD->priv = map; + map->fldrv_priv = priv; + map->fldrv = &jedec_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return MTD; + } + +@@ -344,15 +385,15 @@ + for (I = 0; JEDEC_table[I].jedec != 0; I++) + if (JEDEC_table[I].jedec == Id) + return JEDEC_table + I; +- return 0; ++ return NULL; + } + + // Look for flash using an 8 bit bus interface + static int jedec_probe8(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read8(map,base+x) +- #define flwrite(v,x) map->write8(map,v,base+x) ++ #define flread(x) map_read8(map,base+x) ++ #define flwrite(v,x) map_write8(map,v,base+x) + + const unsigned long AutoSel1 = 0xAA; + const unsigned long AutoSel2 = 0x55; +@@ -411,8 +452,8 @@ + static int jedec_probe32(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read32(map,base+((x)<<2)) +- #define flwrite(v,x) map->write32(map,v,base+((x)<<2)) ++ #define flread(x) map_read32(map,base+((x)<<2)) ++ #define flwrite(v,x) map_write32(map,v,base+((x)<<2)) + + const unsigned long AutoSel1 = 0xAAAAAAAA; + const unsigned long AutoSel2 = 0x55555555; +@@ -488,9 +529,9 @@ + static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { +- struct map_info *map = (struct map_info *)mtd->priv; ++ struct map_info *map = mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -500,8 +541,8 @@ + static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { +- struct map_info *map = (struct map_info *)mtd->priv; +- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; ++ struct map_info *map = mtd->priv; ++ struct jedec_private *priv = map->fldrv_priv; + + *retlen = 0; + while (len > 0) +@@ -514,7 +555,7 @@ + get = priv->bank_fill[0] - offset; + + bank /= priv->bank_fill[0]; +- map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); ++ map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); + + len -= get; + *retlen += get; +@@ -545,15 +586,15 @@ + static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) + { + // Does IO to the currently selected chip +- #define flread(x) map->read8(map,chip->base+((x)<addrshift)) +- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<addrshift)) ++ #define flread(x) map_read8(map,chip->base+((x)<addrshift)) ++ #define flwrite(v,x) map_write8(map,v,chip->base+((x)<addrshift)) + + unsigned long Time = 0; + unsigned long NoTime = 0; + unsigned long start = instr->addr, len = instr->len; + unsigned int I; +- struct map_info *map = (struct map_info *)mtd->priv; +- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; ++ struct map_info *map = mtd->priv; ++ struct jedec_private *priv = map->fldrv_priv; + + // Verify the arguments.. + if (start + len > mtd->size || +@@ -608,7 +649,7 @@ + + /* Poll the flash for erasure completion, specs say this can take as long + as 480 seconds to do all the sectors (for a 2 meg flash). +- Erasure time is dependant on chip age, temp and wear.. */ ++ Erasure time is dependent on chip age, temp and wear.. */ + + /* This being a generic routine assumes a 32 bit bus. It does read32s + and bundles interleved chips into the same grouping. This will work +@@ -651,19 +692,19 @@ + or this is not really flash ;> */ + switch (map->buswidth) { + case 1: +- Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 3: +- Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count = 3; +@@ -699,13 +740,13 @@ + + switch (map->buswidth) { + case 1: +- Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 4: +- Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count++; +@@ -739,8 +780,7 @@ + + //printk("done\n"); + instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + return 0; + + #undef flread +@@ -755,13 +795,13 @@ + size_t *retlen, const u_char *buf) + { + /* Does IO to the currently selected chip. It takes the bank addressing +- base (which is divisable by the chip size) adds the necesary lower bits +- of addrshift (interleve index) and then adds the control register index. */ +- #define flread(x) map->read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) +- #define flwrite(v,x) map->write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) ++ base (which is divisible by the chip size) adds the necessary lower bits ++ of addrshift (interleave index) and then adds the control register index. */ ++ #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) ++ #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) + +- struct map_info *map = (struct map_info *)mtd->priv; +- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; ++ struct map_info *map = mtd->priv; ++ struct jedec_private *priv = map->fldrv_priv; + unsigned long base; + unsigned long off; + size_t save_len = len; +@@ -794,7 +834,7 @@ + // Loop over this page + for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) + { +- unsigned char oldbyte = map->read8(map,base+off); ++ unsigned char oldbyte = map_read8(map,base+off); + unsigned char Last[4]; + unsigned long Count = 0; + +@@ -809,10 +849,10 @@ + flwrite(0xAA,0x555); + flwrite(0x55,0x2AA); + flwrite(0xA0,0x555); +- map->write8(map,*buf,base + off); +- Last[0] = map->read8(map,base + off); +- Last[1] = map->read8(map,base + off); +- Last[2] = map->read8(map,base + off); ++ map_write8(map,*buf,base + off); ++ Last[0] = map_read8(map,base + off); ++ Last[1] = map_read8(map,base + off); ++ Last[2] = map_read8(map,base + off); + + /* Wait for the flash to finish the operation. We store the last 4 + status bytes that have been retrieved so we can determine why +@@ -820,7 +860,7 @@ + failure */ + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && + Count < 10000; Count++) +- Last[Count % 4] = map->read8(map,base + off); ++ Last[Count % 4] = map_read8(map,base + off); + if (Last[(Count - 1) % 4] != *buf) + { + jedec_flash_failed(Last[(Count - 3) % 4]); +--- linux-2.4.21/drivers/mtd/chips/jedec_probe.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/jedec_probe.c +@@ -1,13 +1,16 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id$ ++ $Id$ + See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) + for the standard this probe goes back to. ++ ++ Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + */ + + #include + #include ++#include + #include + #include + #include +@@ -15,7 +18,9 @@ + #include + #include + #include ++#include + ++#include + #include + #include + #include +@@ -24,26 +29,35 @@ + #define MANUFACTURER_AMD 0x0001 + #define MANUFACTURER_ATMEL 0x001f + #define MANUFACTURER_FUJITSU 0x0004 ++#define MANUFACTURER_HYUNDAI 0x00AD + #define MANUFACTURER_INTEL 0x0089 + #define MANUFACTURER_MACRONIX 0x00C2 +-#define MANUFACTURER_ST 0x0020 ++#define MANUFACTURER_PMC 0x009D + #define MANUFACTURER_SST 0x00BF ++#define MANUFACTURER_ST 0x0020 + #define MANUFACTURER_TOSHIBA 0x0098 ++#define MANUFACTURER_WINBOND 0x00da + + + /* AMD */ ++#define AM29DL800BB 0x22C8 ++#define AM29DL800BT 0x224A ++ + #define AM29F800BB 0x2258 + #define AM29F800BT 0x22D6 ++#define AM29LV400BB 0x22BA ++#define AM29LV400BT 0x22B9 + #define AM29LV800BB 0x225B + #define AM29LV800BT 0x22DA + #define AM29LV160DT 0x22C4 + #define AM29LV160DB 0x2249 + #define AM29F017D 0x003D +-#define AM29F016 0x00AD ++#define AM29F016D 0x00AD + #define AM29F080 0x00D5 + #define AM29F040 0x00A4 + #define AM29LV040B 0x004F + #define AM29F032B 0x0041 ++#define AM29F002T 0x00B0 + + /* Atmel */ + #define AT49BV512 0x0003 +@@ -54,6 +68,7 @@ + #define AT49BV32XT 0x00C9 + + /* Fujitsu */ ++#define MBM29F040C 0x00A4 + #define MBM29LV650UE 0x22D7 + #define MBM29LV320TE 0x22F6 + #define MBM29LV320BE 0x22F9 +@@ -61,6 +76,11 @@ + #define MBM29LV160BE 0x2249 + #define MBM29LV800BA 0x225B + #define MBM29LV800TA 0x22DA ++#define MBM29LV400TC 0x22B9 ++#define MBM29LV400BC 0x22BA ++ ++/* Hyundai */ ++#define HY29F002T 0x00B0 + + /* Intel */ + #define I28F004B3T 0x00d4 +@@ -87,29 +107,46 @@ + #define I82802AC 0x00ac + + /* Macronix */ ++#define MX29LV040C 0x004F + #define MX29LV160T 0x22C4 + #define MX29LV160B 0x2249 + #define MX29F016 0x00AD ++#define MX29F002T 0x00B0 + #define MX29F004T 0x0045 + #define MX29F004B 0x0046 + ++/* PMC */ ++#define PM49FL002 0x006D ++#define PM49FL004 0x006E ++#define PM49FL008 0x006A ++ + /* ST - www.st.com */ +-#define M29W800T 0x00D7 ++#define M29W800DT 0x00D7 ++#define M29W800DB 0x005B + #define M29W160DT 0x22C4 + #define M29W160DB 0x2249 + #define M29W040B 0x00E3 ++#define M50FW040 0x002C ++#define M50FW080 0x002D ++#define M50FW016 0x002E ++#define M50LPW080 0x002F + + /* SST */ ++#define SST29EE020 0x0010 ++#define SST29LE020 0x0012 + #define SST29EE512 0x005d + #define SST29LE512 0x003d + #define SST39LF800 0x2781 + #define SST39LF160 0x2782 ++#define SST39VF1601 0x234b + #define SST39LF512 0x00D4 + #define SST39LF010 0x00D5 + #define SST39LF020 0x00D6 + #define SST39LF040 0x00D7 + #define SST39SF010A 0x00B5 + #define SST39SF020A 0x00B6 ++#define SST49LF004B 0x0060 ++#define SST49LF008A 0x005a + #define SST49LF030A 0x001C + #define SST49LF040A 0x0051 + #define SST49LF080A 0x005B +@@ -122,16 +159,93 @@ + #define TC58FVT641 0x0093 + #define TC58FVB641 0x0095 + ++/* Winbond */ ++#define W49V002A 0x00b0 ++ ++ ++/* ++ * Unlock address sets for AMD command sets. ++ * Intel command sets use the MTD_UADDR_UNNECESSARY. ++ * Each identifier, except MTD_UADDR_UNNECESSARY, and ++ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[]. ++ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure ++ * initialization need not require initializing all of the ++ * unlock addresses for all bit widths. ++ */ ++enum uaddr { ++ MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */ ++ MTD_UADDR_0x0555_0x02AA, ++ MTD_UADDR_0x0555_0x0AAA, ++ MTD_UADDR_0x5555_0x2AAA, ++ MTD_UADDR_0x0AAA_0x0555, ++ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ ++ MTD_UADDR_UNNECESSARY, /* Does not require any address */ ++}; ++ ++ ++struct unlock_addr { ++ u32 addr1; ++ u32 addr2; ++}; ++ ++ ++/* ++ * I don't like the fact that the first entry in unlock_addrs[] ++ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore, ++ * should not be used. The problem is that structures with ++ * initializers have extra fields initialized to 0. It is _very_ ++ * desireable to have the unlock address entries for unsupported ++ * data widths automatically initialized - that means that ++ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here ++ * must go unused. ++ */ ++static const struct unlock_addr unlock_addrs[] = { ++ [MTD_UADDR_NOT_SUPPORTED] = { ++ .addr1 = 0xffff, ++ .addr2 = 0xffff ++ }, ++ ++ [MTD_UADDR_0x0555_0x02AA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x02aa ++ }, ++ ++ [MTD_UADDR_0x0555_0x0AAA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x0aaa ++ }, ++ ++ [MTD_UADDR_0x5555_0x2AAA] = { ++ .addr1 = 0x5555, ++ .addr2 = 0x2aaa ++ }, ++ ++ [MTD_UADDR_0x0AAA_0x0555] = { ++ .addr1 = 0x0AAA, ++ .addr2 = 0x0555 ++ }, ++ ++ [MTD_UADDR_DONT_CARE] = { ++ .addr1 = 0x0000, /* Doesn't matter which address */ ++ .addr2 = 0x0000 /* is used - must be last entry */ ++ }, ++ ++ [MTD_UADDR_UNNECESSARY] = { ++ .addr1 = 0x0000, ++ .addr2 = 0x0000 ++ } ++}; ++ + + struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const int DevSize; +- const int InterfaceDesc; + const int NumEraseRegions; + const int CmdSet; +- const ulong regions[4]; ++ const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ ++ const ulong regions[6]; + }; + + #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) +@@ -145,760 +259,1434 @@ + #define SIZE_4MiB 22 + #define SIZE_8MiB 23 + ++ ++/* ++ * Please keep this list ordered by manufacturer! ++ * Fortunately, the list isn't searched often and so a ++ * slow, linear search isn't so bad. ++ */ + static const struct amd_flash_info jedec_table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F032B, +- name: "AMD AM29F032B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,64) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F032B, ++ .name = "AMD AM29F032B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,64) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BB, ++ .name = "AMD AM29LV400BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BT, ++ .name = "AMD AM29LV400BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ ERASEINFO(0x10000,15), + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB321, +- name: "Toshiba TC58FVB321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++/* add DL */ ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29DL800BB, ++ .name = "AMD AM29DL800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 6, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,4), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x10000,14) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT321, +- name: "Toshiba TC58FVT321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29DL800BT, ++ .name = "AMD AM29DL800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 6, ++ .regions = { ++ ERASEINFO(0x10000,14), ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,4), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x04000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,15), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F017D, ++ .name = "AMD AM29F017D", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F016D, ++ .name = "AMD AM29F016D", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F080, ++ .name = "AMD AM29F080", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F040, ++ .name = "AMD AM29F040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV040B, ++ .name = "AMD AM29LV040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F002T, ++ .name = "AMD AM29F002T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,3), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV512, ++ .name = "Atmel AT49BV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT29LV512, ++ .name = "Atmel AT29LV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x80,256), ++ ERASEINFO(0x80,256) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16X, ++ .name = "Atmel AT49BV16X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,31) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16XT, ++ .name = "Atmel AT49BV16XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB641, +- name: "Toshiba TC58FVB641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,127) ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32X, ++ .name = "Atmel AT49BV32X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT641, +- name: "Toshiba TC58FVT641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,127), ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32XT, ++ .name = "Atmel AT49BV32XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), + ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV650UE, +- name: "Fujitsu MBM29LV650UE", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,128) ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29F040C, ++ .name = "Fujitsu MBM29F040C", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320TE, +- name: "Fujitsu MBM29LV320TE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV650UE, ++ .name = "Fujitsu MBM29LV650UE", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,128) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320TE, ++ .name = "Fujitsu MBM29LV320TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), + ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320BE, +- name: "Fujitsu MBM29LV320BE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320BE, ++ .name = "Fujitsu MBM29LV320BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BA, +- name: "Fujitsu MBM29LV800BA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BA, ++ .name = "Fujitsu MBM29LV800BA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800TA, +- name: "Fujitsu MBM29LV800TA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800TA, ++ .name = "Fujitsu MBM29LV800TA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400BC, ++ .name = "Fujitsu MBM29LV400BC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ ERASEINFO(0x10000,7) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400TC, ++ .name = "Fujitsu MBM29LV400TC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_HYUNDAI, ++ .dev_id = HY29F002T, ++ .name = "Hyundai HY29F002T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ ERASEINFO(0x04000,1), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3B, +- name: "Intel 28F004B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3B, ++ .name = "Intel 28F004B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3T, +- name: "Intel 28F004B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3T, ++ .name = "Intel 28F004B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3B, +- name: "Intel 28F400B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3B, ++ .name = "Intel 28F400B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3T, +- name: "Intel 28F400B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3T, ++ .name = "Intel 28F400B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3B, +- name: "Intel 28F008B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3B, ++ .name = "Intel 28F008B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3T, +- name: "Intel 28F008B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3T, ++ .name = "Intel 28F008B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008S5, +- name: "Intel 28F008S5", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008S5, ++ .name = "Intel 28F008S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S5, +- name: "Intel 28F016S5", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S5, ++ .name = "Intel 28F016S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008SA, +- name: "Intel 28F008SA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008SA, ++ .name = "Intel 28F008SA", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 16), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3B, +- name: "Intel 28F800B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3B, ++ .name = "Intel 28F800B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3T, +- name: "Intel 28F800B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3T, ++ .name = "Intel 28F800B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3B, +- name: "Intel 28F016B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3B, ++ .name = "Intel 28F016B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S3, +- name: "Intel I28F016S3", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S3, ++ .name = "Intel I28F016S3", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 32), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3T, +- name: "Intel 28F016B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3T, ++ .name = "Intel 28F016B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3B, +- name: "Intel 28F160B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3B, ++ .name = "Intel 28F160B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3T, +- name: "Intel 28F160B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3T, ++ .name = "Intel 28F160B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3B, +- name: "Intel 28F320B3B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3B, ++ .name = "Intel 28F320B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 63), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3T, +- name: "Intel 28F320B3T", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3T, ++ .name = "Intel 28F320B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 63), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3B, +- name: "Intel 28F640B3B", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3B, ++ .name = "Intel 28F640B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 127), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3T, +- name: "Intel 28F640B3T", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3T, ++ .name = "Intel 28F640B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AB, +- name: "Intel 82802AB", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AB, ++ .name = "Intel 82802AB", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AC, +- name: "Intel 82802AC", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AC, ++ .name = "Intel 82802AC", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV040C, ++ .name = "Macronix MX29LV040C", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160T, ++ .name = "MXIC MX29LV160T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160B, ++ .name = "MXIC MX29LV160B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,31) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F016, ++ .name = "Macronix MX29F016", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004T, ++ .name = "Macronix MX29F004T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ ERASEINFO(0x04000,1), + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004B, ++ .name = "Macronix MX29F004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ ERASEINFO(0x10000,7), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV512, +- name: "Atmel AT49BV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,1) ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F002T, ++ .name = "Macronix MX29F002T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,3), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT29LV512, +- name: "Atmel AT29LV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: { +- ERASEINFO(0x80,256), +- ERASEINFO(0x80,256) ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL002, ++ .name = "PMC Pm49FL002", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 64 ) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16X, +- name: "Atmel AT49BV16X", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,31) ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL004, ++ .name = "PMC Pm49FL004", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 128 ) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16XT, +- name: "Atmel AT49BV16XT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL008, ++ .name = "PMC Pm49FL008", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 256 ) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32X, +- name: "Atmel AT49BV32X", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF512, ++ .name = "SST 39LF512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,16), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32XT, +- name: "Atmel AT49BV32XT", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF010, ++ .name = "SST 39LF010", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F017D, +- name: "AMD AM29F017D", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST29EE020, ++ .name = "SST 29EE020", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_SST_PAGE, ++ .NumEraseRegions= 1, ++ regions: {ERASEINFO(0x01000,64), + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F016, +- name: "AMD AM29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST29LE020, ++ .name = "SST 29LE020", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_SST_PAGE, ++ .NumEraseRegions= 1, ++ regions: {ERASEINFO(0x01000,64), + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F080, +- name: "AMD AM29F080", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF020, ++ .name = "SST 39LF020", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F040, +- name: "AMD AM29F040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF040, ++ .name = "SST 39LF040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV040B, +- name: "AMD AM29LV040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF010A, ++ .name = "SST 39SF010A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W040B, +- name: "ST M29W040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF020A, ++ .name = "SST 39SF020A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160T, +- name: "MXIC MX29LV160T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF004B, ++ .name = "SST 49LF004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF008A, ++ .name = "SST 49LF008A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,256), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF030A, ++ .name = "SST 49LF030A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,96), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF040A, ++ .name = "SST 49LF040A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF080A, ++ .name = "SST 49LF080A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,256), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, /* should be CFI */ ++ .dev_id = SST39LF160, ++ .name = "SST 39LF160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x1000,256), ++ ERASEINFO(0x1000,256) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, /* should be CFI */ ++ .dev_id = SST39VF1601, ++ .name = "SST 39VF1601", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x1000,256), ++ ERASEINFO(0x1000,256) ++ } ++ ++ }, { ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DT, ++ .name = "ST M29W800DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160B, +- name: "MXIC MX29LV160B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DB, ++ .name = "ST M29W800DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) +- } +- }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F016, +- name: "Macronix MX29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), ++ ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004T, +- name: "Macronix MX29F004T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,7), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004B, +- name: "Macronix MX29F004B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,7), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF512, +- name: "SST 39LF512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,16), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W040B, ++ .name = "ST M29W040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF010, +- name: "SST 39LF010", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M50FW040, ++ .name = "ST M50FW040", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF020, +- name: "SST 39LF020", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M50FW080, ++ .name = "ST M50FW080", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF040, +- name: "SST 39LF040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M50FW016, ++ .name = "ST M50FW016", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF010A, +- name: "SST 39SF010A", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M50LPW080, ++ .name = "ST M50LPW080", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF020A, +- name: "SST 39SF020A", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF030A, +- name: "SST 49LF030A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,96), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF040A, +- name: "SST 49LF040A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB321, ++ .name = "Toshiba TC58FVB321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF080A, +- name: "SST 49LF080A", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,256), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT321, ++ .name = "Toshiba TC58FVT321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB641, ++ .name = "Toshiba TC58FVB641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,127) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT641, ++ .name = "Toshiba TC58FVT641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,127), ++ ERASEINFO(0x02000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_WINBOND, ++ .dev_id = W49V002A, ++ .name = "Winbond W49V002A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000, 3), ++ ERASEINFO(0x08000, 1), ++ ERASEINFO(0x02000, 2), ++ ERASEINFO(0x04000, 1), + } + } + }; +@@ -907,48 +1695,94 @@ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); + + static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + +-struct mtd_info *jedec_probe(struct map_info *map); ++static struct mtd_info *jedec_probe(struct map_info *map); + + static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, + struct cfi_private *cfi) + { +- u32 result, mask; ++ map_word result; ++ unsigned long mask; ++ u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type); + mask = (1 << (cfi->device_type * 8)) -1; +- result = cfi_read(map, base); +- result &= mask; +- return result; ++ result = map_read(map, base + ofs); ++ return result.x[0] & mask; + } + + static inline u32 jedec_read_id(struct map_info *map, __u32 base, + struct cfi_private *cfi) + { +- int osf; +- u32 result, mask; +- osf = cfi->interleave *cfi->device_type; ++ map_word result; ++ unsigned long mask; ++ u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type); + mask = (1 << (cfi->device_type * 8)) -1; +- result = cfi_read(map, base + osf); +- result &= mask; +- return result; ++ result = map_read(map, base + ofs); ++ return result.x[0] & mask; + } + + static inline void jedec_reset(u32 base, struct map_info *map, + struct cfi_private *cfi) + { + /* Reset */ +- cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ /* after checking the datasheets for SST, MACRONIX and ATMEL ++ * (oh and incidentaly the jedec spec - 3.5.3.3) the reset ++ * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at ++ * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips ++ * as they will ignore the writes and dont care what address ++ * the F0 is written to */ ++ if(cfi->addr_unlock1) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "reset unlock called %x %x \n", ++ cfi->addr_unlock1,cfi->addr_unlock2); ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); ++ } ++ ++ cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + /* Some misdesigned intel chips do not respond for 0xF0 for a reset, + * so ensure we're in read mode. Send both the Intel and the AMD command + * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so + * this should be safe. + */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ /* FIXME - should have reset delay before continuing */ ++} ++ ++ ++static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type) ++{ ++ int uaddr_idx; ++ __u8 uaddr = MTD_UADDR_NOT_SUPPORTED; ++ ++ switch ( device_type ) { ++ case CFI_DEVICETYPE_X8: uaddr_idx = 0; break; ++ case CFI_DEVICETYPE_X16: uaddr_idx = 1; break; ++ case CFI_DEVICETYPE_X32: uaddr_idx = 2; break; ++ default: ++ printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n", ++ __func__, device_type); ++ goto uaddr_done; ++ } ++ ++ uaddr = finfo->uaddr[uaddr_idx]; + ++ if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { ++ /* ASSERT("The unlock addresses for non-8-bit mode ++ are bollocks. We don't really need an array."); */ ++ uaddr = finfo->uaddr[0]; ++ } ++ ++ uaddr_done: ++ return uaddr; + } ++ ++ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) + { + int i,num_erase_regions; ++ __u8 uaddr; + + printk("Found: %s\n",jedec_table[index].name); + +@@ -970,42 +1804,174 @@ + for (i=0; icfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; + } +- p_cfi->cmdset_priv = 0; ++ p_cfi->cmdset_priv = NULL; ++ ++ /* This may be redundant for some cases, but it doesn't hurt */ ++ p_cfi->mfr = jedec_table[index].mfr_id; ++ p_cfi->id = jedec_table[index].dev_id; ++ ++ uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ kfree( p_cfi->cfiq ); ++ return 0; ++ } ++ ++ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; ++ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; ++ + return 1; /* ok */ + } + +-static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++ ++/* ++ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing ++ * the mapped address, unlock addresses, and proper chip ID. This function ++ * attempts to minimize errors. It is doubtfull that this probe will ever ++ * be perfect - consequently there should be some module parameters that ++ * could be manually specified to force the chip info. ++ */ ++static inline int jedec_match( __u32 base, ++ struct map_info *map, ++ struct cfi_private *cfi, ++ const struct amd_flash_info *finfo ) + { +- int i; +- int unlockpass = 0; ++ int rc = 0; /* failure until all tests pass */ ++ u32 mfr, id; ++ __u8 uaddr; + +- if (!cfi->numchips) { ++ /* ++ * The IDs must match. For X16 and X32 devices operating in ++ * a lower width ( X8 or X16 ), the device ID's are usually just ++ * the lower byte(s) of the larger device ID for wider mode. If ++ * a part is found that doesn't fit this assumption (device id for ++ * smaller width mode is completely unrealated to full-width mode) ++ * then the jedec_table[] will have to be augmented with the IDs ++ * for different widths. ++ */ + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: +- cfi->addr_unlock1 = 0x555; +- cfi->addr_unlock2 = 0x2aa; ++ mfr = (__u8)finfo->mfr_id; ++ id = (__u8)finfo->dev_id; ++ ++ /* bjd: it seems that if we do this, we can end up ++ * detecting 16bit flashes as an 8bit device, even though ++ * there aren't. ++ */ ++ if (finfo->dev_id > 0xff) { ++ DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n", ++ __func__); ++ goto match_done; ++ } + break; + case CFI_DEVICETYPE_X16: +- cfi->addr_unlock1 = 0xaaa; +- if (map->buswidth == cfi->interleave) { +- /* X16 chip(s) in X8 mode */ +- cfi->addr_unlock2 = 0x555; +- } else { +- cfi->addr_unlock2 = 0x554; +- } ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u16)finfo->dev_id; + break; + case CFI_DEVICETYPE_X32: +- cfi->addr_unlock1 = 0x1555; +- cfi->addr_unlock2 = 0xaaa; ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u32)finfo->dev_id; + break; + default: +- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type); +- return 0; ++ printk(KERN_WARNING ++ "MTD %s(): Unsupported device type %d\n", ++ __func__, cfi->device_type); ++ goto match_done; + } ++ if ( cfi->mfr != mfr || cfi->id != id ) { ++ goto match_done; ++ } ++ ++ /* the part size must fit in the memory window */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", ++ __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); ++ if ( base + cfi_interleave(cfi) * ( 1 << finfo->DevSize ) > map->size ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", ++ __func__, finfo->mfr_id, finfo->dev_id, ++ 1 << finfo->DevSize ); ++ goto match_done; ++ } ++ ++ uaddr = finfo_uaddr(finfo, cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ goto match_done; ++ } ++ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", ++ __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); ++ if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr ++ && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 || ++ unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x did not match\n", ++ __func__, ++ unlock_addrs[uaddr].addr1, ++ unlock_addrs[uaddr].addr2); ++ goto match_done; ++ } ++ ++ /* ++ * Make sure the ID's dissappear when the device is taken out of ++ * ID mode. The only time this should fail when it should succeed ++ * is when the ID's are written as data to the same ++ * addresses. For this rare and unfortunate case the chip ++ * cannot be probed correctly. ++ * FIXME - write a driver that takes all of the chip info as ++ * module parameters, doesn't probe but forces a load. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): check ID's disappear when not in ID mode\n", ++ __func__ ); ++ jedec_reset( base, map, cfi ); ++ mfr = jedec_read_mfr( map, base, cfi ); ++ id = jedec_read_id( map, base, cfi ); ++ if ( mfr == cfi->mfr && id == cfi->id ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n" ++ "You might need to manually specify JEDEC parameters.\n", ++ __func__, cfi->mfr, cfi->id ); ++ goto match_done; ++ } ++ ++ /* all tests passed - mark as success */ ++ rc = 1; ++ ++ /* ++ * Put the device back in ID mode - only need to do this if we ++ * were truly frobbing a real device. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); ++ if(cfi->addr_unlock1) { ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); + } ++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); ++ /* FIXME - should have a delay before continuing */ ++ ++ match_done: ++ return rc; ++} ++ ++ ++static int jedec_probe_chip(struct map_info *map, __u32 base, ++ unsigned long *chip_map, struct cfi_private *cfi) ++{ ++ int i; ++ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; ++ u32 probe_offset1, probe_offset2; + + retry: ++ if (!cfi->numchips) { ++ uaddr_idx++; ++ ++ if (MTD_UADDR_UNNECESSARY == uaddr_idx) ++ return 0; ++ ++ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; ++ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; ++ } ++ + /* Make certain we aren't probing past the end of map */ + if (base >= map->size) { + printk(KERN_NOTICE +@@ -1014,19 +1980,19 @@ + return 0; + + } +- if ((base + cfi->addr_unlock1) >= map->size) { +- printk(KERN_NOTICE +- "Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", +- base, cfi->addr_unlock1, map->size -1); +- +- return 0; +- } +- if ((base + cfi->addr_unlock2) >= map->size) { +- printk(KERN_NOTICE +- "Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", +- base, cfi->addr_unlock2, map->size -1); +- return 0; +- ++ /* Ensure the unlock addresses we try stay inside the map */ ++ probe_offset1 = cfi_build_cmd_addr( ++ cfi->addr_unlock1, ++ cfi_interleave(cfi), ++ cfi->device_type); ++ probe_offset2 = cfi_build_cmd_addr( ++ cfi->addr_unlock1, ++ cfi_interleave(cfi), ++ cfi->device_type); ++ if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || ++ ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) ++ { ++ goto retry; + } + + /* Reset */ +@@ -1034,10 +2000,11 @@ + + /* Autoselect Mode */ + if(cfi->addr_unlock1) { +- cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); + } +- cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); ++ /* FIXME - should have a delay before continuing */ + + if (!cfi->numchips) { + /* This is the first time we're called. Set up the CFI +@@ -1045,26 +2012,21 @@ + + cfi->mfr = jedec_read_mfr(map, base, cfi); + cfi->id = jedec_read_id(map, base, cfi); +- printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", +- cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "Search for id:(%02x %02x) interleave(%d) type(%d)\n", ++ cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type); + for (i=0; imfr == jedec_table[i].mfr_id && +- cfi->id == jedec_table[i].dev_id) { ++ if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n", ++ __func__, cfi->mfr, cfi->id, ++ cfi->addr_unlock1, cfi->addr_unlock2 ); + if (!cfi_jedec_setup(cfi, i)) + return 0; + goto ok_out; + } + } +- switch(unlockpass++) { +- case 0: +- cfi->addr_unlock1 |= cfi->addr_unlock1 << 4; +- cfi->addr_unlock2 |= cfi->addr_unlock2 << 4; +- goto retry; +- case 1: +- cfi->addr_unlock1 = cfi->addr_unlock2 = 0; + goto retry; +- } +- return 0; + } else { + __u16 mfr; + __u16 id; +@@ -1081,21 +2043,24 @@ + } + } + +- /* Check each previous chip to see if it's an alias */ +- for (i=0; inumchips; i++) { +- /* This chip should be in read mode if it's one +- we've already touched. */ +- if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && +- jedec_read_id(map, chips[i].start, cfi) == cfi->id) { ++ /* Check each previous chip locations to see if it's an alias */ ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ continue; /* Skip location; no valid chip at this address */ ++ } ++ start = i << cfi->chipshift; ++ if (jedec_read_mfr(map, start, cfi) == cfi->mfr && ++ jedec_read_id(map, start, cfi) == cfi->id) { + /* Eep. This chip also looks like it's in autoselect mode. + Is it an alias for the new one? */ +- jedec_reset(chips[i].start, map, cfi); ++ jedec_reset(start, map, cfi); + + /* If the device IDs go away, it's an alias */ + if (jedec_read_mfr(map, base, cfi) != cfi->mfr || + jedec_read_id(map, base, cfi) != cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + +@@ -1107,7 +2072,7 @@ + if (jedec_read_mfr(map, base, cfi) == cfi->mfr && + jedec_read_id(map, base, cfi) == cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -1115,32 +2080,26 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + ok_out: + /* Put it back into Read Mode */ + jedec_reset(base, map, cfi); + +- printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", +- map->name, cfi->interleave, cfi->device_type*8, base, +- map->buswidth*8); ++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", ++ map->name, cfi_interleave(cfi), cfi->device_type*8, base, ++ map->bankwidth*8); + + return 1; + } + + static struct chip_probe jedec_chip_probe = { +- name: "JEDEC", +- probe_chip: jedec_probe_chip ++ .name = "JEDEC", ++ .probe_chip = jedec_probe_chip + }; + +-struct mtd_info *jedec_probe(struct map_info *map) ++static struct mtd_info *jedec_probe(struct map_info *map) + { + /* + * Just use the generic probe stuff to call our CFI-specific +@@ -1150,12 +2109,12 @@ + } + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec_probe", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec_probe", ++ .module = THIS_MODULE + }; + +-int __init jedec_probe_init(void) ++static int __init jedec_probe_init(void) + { + register_mtd_chip_driver(&jedec_chipdrv); + return 0; +--- linux-2.4.21/drivers/mtd/chips/map_absent.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/map_absent.c +@@ -1,7 +1,7 @@ + /* + * Common code to handle absent "placeholder" devices + * Copyright 2001 Resilience Corporation +- * $Id$ ++ * $Id$ + * + * This map driver is used to allocate "placeholder" MTD + * devices on systems that have socketed/removable media. +@@ -23,9 +23,10 @@ + #include + #include + #include +- ++#include ++#include + #include +- ++#include + + static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -36,10 +37,10 @@ + + + static struct mtd_chip_driver map_absent_chipdrv = { +- probe: map_absent_probe, +- destroy: map_absent_destroy, +- name: "map_absent", +- module: THIS_MODULE ++ .probe = map_absent_probe, ++ .destroy = map_absent_destroy, ++ .name = "map_absent", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_absent_probe(struct map_info *map) +@@ -65,7 +66,7 @@ + mtd->flags = 0; + mtd->erasesize = PAGE_SIZE; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -97,7 +98,7 @@ + /* nop */ + } + +-int __init map_absent_init(void) ++static int __init map_absent_init(void) + { + register_mtd_chip_driver(&map_absent_chipdrv); + return 0; +--- linux-2.4.21/drivers/mtd/chips/map_ram.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/map_ram.c +@@ -1,7 +1,7 @@ + /* + * Common code to handle map devices which are simple RAM + * (C) 2000 Red Hat. GPL'd. +- * $Id$ ++ * $Id$ + */ + + #include +@@ -11,8 +11,10 @@ + #include + #include + #include +- ++#include ++#include + #include ++#include + + + static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -23,9 +25,9 @@ + + + static struct mtd_chip_driver mapram_chipdrv = { +- probe: map_ram_probe, +- name: "map_ram", +- module: THIS_MODULE ++ .probe = map_ram_probe, ++ .name = "map_ram", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_ram_probe(struct map_info *map) +@@ -34,21 +36,21 @@ + + /* Check the first byte is RAM */ + #if 0 +- map->write8(map, 0x55, 0); +- if (map->read8(map, 0) != 0x55) ++ map_write8(map, 0x55, 0); ++ if (map_read8(map, 0) != 0x55) + return NULL; + +- map->write8(map, 0xAA, 0); +- if (map->read8(map, 0) != 0xAA) ++ map_write8(map, 0xAA, 0); ++ if (map_read8(map, 0) != 0xAA) + return NULL; + + /* Check the last byte is RAM */ +- map->write8(map, 0x55, map->size-1); +- if (map->read8(map, map->size-1) != 0x55) ++ map_write8(map, 0x55, map->size-1); ++ if (map_read8(map, map->size-1) != 0x55) + return NULL; + +- map->write8(map, 0xAA, map->size-1); +- if (map->read8(map, map->size-1) != 0xAA) ++ map_write8(map, 0xAA, map->size-1); ++ if (map_read8(map, map->size-1) != 0xAA) + return NULL; + #endif + /* OK. It seems to be RAM. */ +@@ -74,25 +76,25 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + + + static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { +- struct map_info *map = (struct map_info *)mtd->priv; ++ struct map_info *map = mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } + + static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- struct map_info *map = (struct map_info *)mtd->priv; ++ struct map_info *map = mtd->priv; + +- map->copy_to(map, to, buf, len); ++ map_copy_to(map, to, buf, len); + *retlen = len; + return 0; + } +@@ -101,14 +103,18 @@ + { + /* Yeah, it's inefficient. Who cares? It's faster than a _real_ + flash erase. */ +- struct map_info *map = (struct map_info *)mtd->priv; ++ struct map_info *map = mtd->priv; ++ map_word allff; + unsigned long i; + +- for (i=0; ilen; i++) +- map->write8(map, 0xFF, instr->addr + i); ++ allff = map_word_ff(map); + +- if (instr->callback) +- instr->callback(instr); ++ for (i=0; ilen; i += map_bankwidth(map)) ++ map_write(map, allff, instr->addr + i); ++ ++ instr->state = MTD_ERASE_DONE; ++ ++ mtd_erase_callback(instr); + + return 0; + } +@@ -118,7 +124,7 @@ + /* Nothing to see here */ + } + +-int __init map_ram_init(void) ++static int __init map_ram_init(void) + { + register_mtd_chip_driver(&mapram_chipdrv); + return 0; +--- linux-2.4.21/drivers/mtd/chips/map_rom.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/map_rom.c +@@ -1,10 +1,9 @@ + /* + * Common code to handle map devices which are simple ROM + * (C) 2000 Red Hat. GPL'd. +- * $Id$ ++ * $Id$ + */ + +-#include + #include + #include + #include +@@ -12,21 +11,23 @@ + #include + #include + #include +- ++#include ++#include + #include ++#include + + static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static void maprom_nop (struct mtd_info *); +-struct mtd_info *map_rom_probe(struct map_info *map); ++static struct mtd_info *map_rom_probe(struct map_info *map); + + static struct mtd_chip_driver maprom_chipdrv = { +- probe: map_rom_probe, +- name: "map_rom", +- module: THIS_MODULE ++ .probe = map_rom_probe, ++ .name = "map_rom", ++ .module = THIS_MODULE + }; + +-struct mtd_info *map_rom_probe(struct map_info *map) ++static struct mtd_info *map_rom_probe(struct map_info *map) + { + struct mtd_info *mtd; + +@@ -49,16 +50,16 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + + + static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { +- struct map_info *map = (struct map_info *)mtd->priv; ++ struct map_info *map = mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -74,7 +75,7 @@ + return -EIO; + } + +-int __init map_rom_init(void) ++static int __init map_rom_init(void) + { + register_mtd_chip_driver(&maprom_chipdrv); + return 0; +--- linux-2.4.21/drivers/mtd/chips/sharp.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/chips/sharp.c +@@ -4,7 +4,7 @@ + * Copyright 2000,2001 David A. Schleef + * 2000,2001 Lineo, Inc. + * +- * $Id$ ++ * $Id$ + * + * Devices supported: + * LH28F016SCT Symmetrical block flash memory, 2Mx8 +@@ -22,14 +22,15 @@ + + #include + #include +-#include + #include + #include + #include + #include + #include ++#include + #include + #include ++#include + + #define CMD_RESET 0xffffffff + #define CMD_READ_ID 0x90909090 +@@ -98,10 +99,10 @@ + static void sharp_destroy(struct mtd_info *mtd); + + static struct mtd_chip_driver sharp_chipdrv = { +- probe: sharp_probe, +- destroy: sharp_destroy, +- name: "sharp", +- module: THIS_MODULE ++ .probe = sharp_probe, ++ .destroy = sharp_destroy, ++ .name = "sharp", ++ .module = THIS_MODULE + }; + + +@@ -116,8 +117,10 @@ + return NULL; + + sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); +- if(!sharp) ++ if(!sharp) { ++ kfree(mtd); + return NULL; ++ } + + memset(mtd, 0, sizeof(*mtd)); + +@@ -152,7 +155,7 @@ + map->fldrv = &sharp_chipdrv; + map->fldrv_priv = sharp; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -163,12 +166,12 @@ + u32 read0, read4; + int width = 4; + +- tmp = map->read32(map, base+0); ++ tmp = map_read32(map, base+0); + +- map->write32(map, CMD_READ_ID, base+0); ++ map_write32(map, CMD_READ_ID, base+0); + +- read0=map->read32(map, base+0); +- read4=map->read32(map, base+4); ++ read0=map_read32(map, base+0); ++ read4=map_read32(map, base+4); + if(read0 == 0x89898989){ + printk("Looks like sharp flash\n"); + switch(read4){ +@@ -196,10 +199,10 @@ + printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n", + read0,read4); + } +- }else if((map->read32(map, base+0) == CMD_READ_ID)){ ++ }else if((map_read32(map, base+0) == CMD_READ_ID)){ + /* RAM, probably */ + printk("Looks like RAM\n"); +- map->write32(map, tmp, base+0); ++ map_write32(map, tmp, base+0); + }else{ + printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n", + read0,read4); +@@ -221,10 +224,10 @@ + + switch(chip->state){ + case FL_READY: +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + chip->state = FL_STATUS; + case FL_STATUS: +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + //printk("status=%08x\n",status); + + udelay(100); +@@ -252,7 +255,7 @@ + goto retry; + } + +- map->write32(map,CMD_RESET, adr); ++ map_write32(map,CMD_RESET, adr); + + chip->state = FL_READY; + +@@ -293,7 +296,7 @@ + if(ret<0) + break; + +- map->copy_from(map,buf,ofs,thislen); ++ map_copy_from(map,buf,ofs,thislen); + + sharp_release(&sharp->chips[chipnum]); + +@@ -354,17 +357,17 @@ + ret = sharp_wait(map,chip); + + for(try=0;try<10;try++){ +- map->write32(map,CMD_BYTE_WRITE,adr); ++ map_write32(map,CMD_BYTE_WRITE,adr); + /* cpu_to_le32 -> hack to fix the writel be->le conversion */ +- map->write32(map,cpu_to_le32(datum),adr); ++ map_write32(map,cpu_to_le32(datum),adr); + + chip->state = FL_WRITING; + + timeo = jiffies + (HZ/2); + +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + for(i=0;i<100;i++){ +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + } +@@ -377,9 +380,9 @@ + + printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status); + +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + + wake_up(&chip->wq); +@@ -422,8 +425,7 @@ + } + + instr->state = MTD_ERASE_DONE; +- if(instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } +@@ -432,18 +434,18 @@ + unsigned long adr) + { + int ret; +- int timeo; ++ unsigned long timeo; + int status; + DECLARE_WAITQUEUE(wait, current); + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + timeo = jiffies + HZ; + + while(time_before(jiffies, timeo)){ +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY){ + ret = 0; + goto out; +@@ -485,26 +487,26 @@ + sharp_unlock_oneblock(map,chip,adr); + #endif + +- map->write32(map,CMD_BLOCK_ERASE_1,adr); +- map->write32(map,CMD_BLOCK_ERASE_2,adr); ++ map_write32(map,CMD_BLOCK_ERASE_1,adr); ++ map_write32(map,CMD_BLOCK_ERASE_2,adr); + + chip->state = FL_ERASING; + + ret = sharp_do_wait_for_ready(map,chip,adr); + if(ret<0)return ret; + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + //spin_unlock_bh(chip->mutex); + return 0; + } + + printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + + //spin_unlock_bh(chip->mutex); + +@@ -518,17 +520,17 @@ + int i; + int status; + +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); + + udelay(100); + +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + printk("status=%08x\n",status); + + for(i=0;i<1000;i++){ +- //map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ //map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + udelay(100); +@@ -538,13 +540,13 @@ + } + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + return; + } + + printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } + #endif + +--- linux-2.4.21/drivers/mtd/cmdlinepart.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/cmdlinepart.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Read flash partition table from command line + * +@@ -10,7 +10,7 @@ + * mtdparts=[; := :[,] + * := [@offset][][ro] +- * := unique id used in mapping driver/device ++ * := unique name used in mapping driver/device (mtd->name) + * := standard linux memsize OR "-" to denote all remaining space + * := '(' NAME ')' + * +@@ -28,7 +28,6 @@ + + #include + #include +-#include + #include + + /* error message prefix */ +@@ -95,7 +94,7 @@ + if (size < PAGE_SIZE) + { + printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); +- return 0; ++ return NULL; + } + } + +@@ -122,7 +121,7 @@ + if ((p = strchr(name, delim)) == 0) + { + printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); +- return 0; ++ return NULL; + } + name_len = p - name; + s = p + 1; +@@ -149,12 +148,12 @@ + if (size == SIZE_REMAINING) + { + printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); +- return 0; ++ return NULL; + } + /* more partitions follow, parse them */ + if ((parts = newpart(s + 1, &s, num_parts, + this_part + 1, &extra_mem, extra_mem_size)) == 0) +- return 0; ++ return NULL; + } + else + { /* this is the last partition: allocate space for all */ +@@ -167,7 +166,7 @@ + if (!parts) + { + printk(KERN_ERR ERRP "out of memory\n"); +- return 0; ++ return NULL; + } + memset(parts, 0, alloc_size); + extra_mem = (unsigned char *)(parts + *num_parts); +@@ -178,8 +177,7 @@ + parts[this_part].mask_flags = mask_flags; + if (name) + { +- strncpy(extra_mem, name, name_len); +- extra_mem[name_len] = 0; ++ strlcpy(extra_mem, name, name_len + 1); + } + else + { +@@ -258,8 +256,7 @@ + this_mtd->parts = parts; + this_mtd->num_parts = num_parts; + this_mtd->mtd_id = (char*)(this_mtd + 1); +- strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); +- this_mtd->mtd_id[mtd_id_len] = 0; ++ strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); + + /* link into chain */ + this_mtd->next = partitions; +@@ -291,13 +288,14 @@ + * information. It returns partitions for the requested mtd device, or + * the first one in the chain if a NULL mtd_id is passed in. + */ +-int parse_cmdline_partitions(struct mtd_info *master, ++static int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, +- const char *mtd_id) ++ unsigned long origin) + { + unsigned long offset; + int i; + struct cmdline_mtd_partition *part; ++ char *mtd_id = master->name; + + if(!cmdline) + return -EINVAL; +@@ -340,8 +338,10 @@ + * This is the handler for our kernel parameter, called from + * main.c::checksetup(). Note that we can not yet kmalloc() anything, + * so we only save the commandline for later processing. ++ * ++ * This function needs to be visible for bootloaders. + */ +-static int __init mtdpart_setup(char *s) ++int mtdpart_setup(char *s) + { + cmdline = s; + return 1; +@@ -349,7 +349,18 @@ + + __setup("mtdparts=", mtdpart_setup); + +-EXPORT_SYMBOL(parse_cmdline_partitions); ++static struct mtd_part_parser cmdline_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_cmdline_partitions, ++ .name = "cmdlinepart", ++}; ++ ++static int __init cmdline_parser_init(void) ++{ ++ return register_mtd_parser(&cmdline_parser); ++} ++ ++module_init(cmdline_parser_init); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Marius Groeger "); +--- linux-2.4.21/drivers/mtd/devices/Config.in~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/Config.in +@@ -1,6 +1,6 @@ +-# drivers/mtd/maps/Config.in ++# drivers/mtd/devices/Config.in + +-# $Id$ ++# $Id$ + + mainmenu_option next_comment + +@@ -10,22 +10,13 @@ + bool ' PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX + bool ' PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG + fi +-if [ "$CONFIG_DECSTATION" = "y" ]; then ++if [ "$CONFIG_MACH__DECSTATION" = "y" ]; then + dep_tristate ' DEC MS02-NV NVRAM module support' CONFIG_MTD_MS02NV $CONFIG_MTD + fi + dep_tristate ' Uncached system RAM' CONFIG_MTD_SLRAM $CONFIG_MTD + if [ "$CONFIG_SA1100_LART" = "y" ]; then + dep_tristate ' 28F160xx flash driver for LART' CONFIG_MTD_LART $CONFIG_MTD + fi +-if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then +- dep_tristate ' SyncFlash driver for MX1ADS' CONFIG_MTD_SYNCFLASH $CONFIG_MTD +-fi +-if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then +- dep_tristate ' AT91RM9200 DataFlash support' CONFIG_MTD_AT91_DATAFLASH $CONFIG_MTD +- if [ "$CONFIG_MTD_AT91_DATAFLASH" = "y" -o "$CONFIG_MTD_AT91_DATAFLASH" = "m" ]; then +- bool ' Enable DataFlash card? ' CONFIG_MTD_AT91_DATAFLASH_CARD +- fi +-fi + dep_tristate ' Test driver using RAM' CONFIG_MTD_MTDRAM $CONFIG_MTD + if [ "$CONFIG_MTD_MTDRAM" = "y" -o "$CONFIG_MTD_MTDRAM" = "m" ]; then + int 'MTDRAM device size in KiB' CONFIG_MTDRAM_TOTAL_SIZE 4096 +@@ -37,19 +28,29 @@ + dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD + + comment 'Disk-On-Chip Device Drivers' +- dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD +- dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD +- dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD +- if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then ++ dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)' CONFIG_MTD_DOC2000 $CONFIG_MTD ++ dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)' CONFIG_MTD_DOC2001 $CONFIG_MTD ++ dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then + define_bool CONFIG_MTD_DOCPROBE y + else +- if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then + define_bool CONFIG_MTD_DOCPROBE m + else + define_bool CONFIG_MTD_DOCPROBE n + fi + fi + ++ if [ "$CONFIG_MTD_DOCPROBE" = "y" ]; then ++ define_bool CONFIG_MTD_DOCECC y ++ else ++ if [ "$CONFIG_MTD_DOCPROBE" = "m" ]; then ++ define_bool CONFIG_MTD_DOCECC m ++ else ++ define_bool CONFIG_MTD_DOCECC n ++ fi ++ fi ++ + if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then + bool ' Advanced detection options for DiskOnChip' CONFIG_MTD_DOCPROBE_ADVANCED + if [ "$CONFIG_MTD_DOCPROBE_ADVANCED" = "n" ]; then +--- linux-2.4.21/drivers/mtd/devices/Makefile~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/Makefile +@@ -1,27 +1,23 @@ + # +-# linux/drivers/devices/Makefile ++# linux/drivers/maps/Makefile.24 ++# Makefile for obsolete kernels + # +-# $Id$ ++# $Id$ + + O_TARGET := devlink.o ++export-objs := docecc.o + +-# *** BIG UGLY NOTE *** +-# +-# The removal of get_module_symbol() and replacement with +-# inter_module_register() et al has introduced a link order dependency +-# here where previously there was none. We now have to ensure that +-# doc200[01].o are linked before docprobe.o +- +-obj-$(CONFIG_MTD_DOC1000) += doc1000.o + obj-$(CONFIG_MTD_DOC2000) += doc2000.o + obj-$(CONFIG_MTD_DOC2001) += doc2001.o +-obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o ++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o ++obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o ++obj-$(CONFIG_MTD_DOCECC) += docecc.o + obj-$(CONFIG_MTD_SLRAM) += slram.o ++obj-$(CONFIG_MTD_PHRAM) += phram.o + obj-$(CONFIG_MTD_PMC551) += pmc551.o + obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o + obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + obj-$(CONFIG_MTD_LART) += lart.o +-obj-$(CONFIG_MTD_SYNCFLASH) += syncflash.o +-obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o ++obj-$(CONFIG_MTD_BLKMTD) += blkmtd-24.o + + include $(TOPDIR)/Rules.make +--- /dev/null ++++ linux-2.4.21/drivers/mtd/devices/Makefile.common +@@ -0,0 +1,25 @@ ++# ++# linux/drivers/devices/Makefile ++# ++# $Id$ ++ ++# *** BIG UGLY NOTE *** ++# ++# The removal of get_module_symbol() and replacement with ++# inter_module_register() et al has introduced a link order dependency ++# here where previously there was none. We now have to ensure that ++# doc200[01].o are linked before docprobe.o ++ ++obj-$(CONFIG_MTD_DOC2000) += doc2000.o ++obj-$(CONFIG_MTD_DOC2001) += doc2001.o ++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o ++obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o ++obj-$(CONFIG_MTD_DOCECC) += docecc.o ++obj-$(CONFIG_MTD_SLRAM) += slram.o ++obj-$(CONFIG_MTD_PHRAM) += phram.o ++obj-$(CONFIG_MTD_PMC551) += pmc551.o ++obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o ++obj-$(CONFIG_MTD_MTDRAM) += mtdram.o ++obj-$(CONFIG_MTD_LART) += lart.o ++obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o ++obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o +--- /dev/null ++++ linux-2.4.21/drivers/mtd/devices/blkmtd-24.c +@@ -0,0 +1,1056 @@ ++/* ++ * $Id$ ++ * ++ * blkmtd.c - use a block device as a fake MTD ++ * ++ * Author: Simon Evans ++ * ++ * Copyright (C) 2001,2002 Simon Evans ++ * ++ * Licence: GPL ++ * ++ * How it works: ++ * The driver uses raw/io to read/write the device and the page ++ * cache to cache access. Writes update the page cache with the ++ * new data and mark it dirty and add the page into a kiobuf. ++ * When the kiobuf becomes full or the next extry is to an earlier ++ * block in the kiobuf then it is flushed to disk. This allows ++ * writes to remained ordered and gives a small and simple outgoing ++ * write cache. ++ * ++ * It can be loaded Read-Only to prevent erases and writes to the ++ * medium. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_DEBUG ++#ifdef CONFIG_PROC_FS ++# include ++# define BLKMTD_PROC_DEBUG ++ static struct proc_dir_entry *blkmtd_proc; ++#endif ++#endif ++ ++ ++#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) ++#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) ++ ++ ++/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */ ++#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ ++#define VERSION "1.10" ++ ++/* Info for the block device */ ++struct blkmtd_dev { ++ struct list_head list; ++ struct block_device *binding; ++ struct mtd_info mtd_info; ++ struct kiobuf *rd_buf, *wr_buf; ++ long iobuf_locks; ++ struct semaphore wrbuf_mutex; ++}; ++ ++ ++/* Static info about the MTD, used in cleanup_module */ ++static LIST_HEAD(blkmtd_device_list); ++ ++ ++static void blkmtd_sync(struct mtd_info *mtd); ++ ++#define MAX_DEVICES 4 ++ ++/* Module parameters passed by insmod/modprobe */ ++char *device[MAX_DEVICES]; /* the block device to use */ ++int erasesz[MAX_DEVICES]; /* optional default erase size */ ++int ro[MAX_DEVICES]; /* optional read only flag */ ++int sync; ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Simon Evans "); ++MODULE_DESCRIPTION("Emulate an MTD using a block device"); ++MODULE_PARM(device, "1-4s"); ++MODULE_PARM_DESC(device, "block device to use"); ++MODULE_PARM(erasesz, "1-4i"); ++MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); ++MODULE_PARM(ro, "1-4i"); ++MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); ++MODULE_PARM(sync, "i"); ++MODULE_PARM_DESC(sync, "1=Synchronous writes"); ++ ++ ++/** ++ * read_pages - read in pages via the page cache ++ * @dev: device to read from ++ * @pagenrs: list of page numbers wanted ++ * @pagelst: storage for struce page * pointers ++ * @pages: count of pages wanted ++ * ++ * Read pages, getting them from the page cache if available ++ * else reading them in from disk if not. pagelst must be preallocated ++ * to hold the page count. ++ */ ++static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages) ++{ ++ kdev_t kdev; ++ struct page *page; ++ int cnt = 0; ++ struct kiobuf *iobuf; ++ int err = 0; ++ ++ if(!dev) { ++ err("read_pages: PANIC dev == NULL"); ++ return -EIO; ++ } ++ kdev = to_kdev_t(dev->binding->bd_dev); ++ ++ DEBUG(2, "read_pages: reading %d pages\n", pages); ++ if(test_and_set_bit(0, &dev->iobuf_locks)) { ++ err = alloc_kiovec(1, &iobuf); ++ if (err) { ++ crit("cant allocate kiobuf"); ++ return -ENOMEM; ++ } ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); ++ if(iobuf->blocks == NULL) { ++ crit("cant allocate iobuf blocks"); ++ free_kiovec(1, &iobuf); ++ return -ENOMEM; ++ } ++#endif ++ } else { ++ iobuf = dev->rd_buf; ++ } ++ ++ iobuf->nr_pages = 0; ++ iobuf->length = 0; ++ iobuf->offset = 0; ++ iobuf->locked = 1; ++ ++ for(cnt = 0; cnt < pages; cnt++) { ++ page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); ++ pagelst[cnt] = page; ++ if(!Page_Uptodate(page)) { ++ iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; ++ iobuf->maplist[iobuf->nr_pages++] = page; ++ } ++ } ++ ++ if(iobuf->nr_pages) { ++ iobuf->length = iobuf->nr_pages << PAGE_SHIFT; ++ err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); ++ DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err); ++ if(err < 0) { ++ while(pages--) { ++ ClearPageUptodate(pagelst[pages]); ++ unlock_page(pagelst[pages]); ++ page_cache_release(pagelst[pages]); ++ } ++ } else { ++ while(iobuf->nr_pages--) { ++ SetPageUptodate(iobuf->maplist[iobuf->nr_pages]); ++ } ++ err = 0; ++ } ++ } ++ ++ ++ if(iobuf != dev->rd_buf) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ kfree(iobuf->blocks); ++#endif ++ free_kiovec(1, &iobuf); ++ } else { ++ clear_bit(0, &dev->iobuf_locks); ++ } ++ DEBUG(2, "read_pages: done, err = %d\n", err); ++ return err; ++} ++ ++ ++/** ++ * commit_pages - commit pages in the writeout kiobuf to disk ++ * @dev: device to write to ++ * ++ * If the current dev has pages in the dev->wr_buf kiobuf, ++ * they are written to disk using brw_kiovec() ++ */ ++static int commit_pages(struct blkmtd_dev *dev) ++{ ++ struct kiobuf *iobuf = dev->wr_buf; ++ kdev_t kdev = to_kdev_t(dev->binding->bd_dev); ++ int err = 0; ++ ++ iobuf->length = iobuf->nr_pages << PAGE_SHIFT; ++ iobuf->locked = 1; ++ if(iobuf->length) { ++ int i; ++ DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages); ++ /* Check all the pages are dirty and lock them */ ++ for(i = 0; i < iobuf->nr_pages; i++) { ++ struct page *page = iobuf->maplist[i]; ++ BUG_ON(!PageDirty(page)); ++ lock_page(page); ++ } ++ err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); ++ DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err); ++ while(iobuf->nr_pages) { ++ struct page *page = iobuf->maplist[--iobuf->nr_pages]; ++ ClearPageDirty(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ page_cache_release(page); ++ } ++ } ++ ++ DEBUG(2, "blkmtd: sync: end, err = %d\n", err); ++ iobuf->offset = 0; ++ iobuf->nr_pages = 0; ++ iobuf->length = 0; ++ return err; ++} ++ ++ ++/** ++ * write_pages - write block of data to device via the page cache ++ * @dev: device to write to ++ * @buf: data source or NULL if erase (output is set to 0xff) ++ * @to: offset into output device ++ * @len: amount to data to write ++ * @retlen: amount of data written ++ * ++ * Grab pages from the page cache and fill them with the source data. ++ * Non page aligned start and end result in a readin of the page and ++ * part of the page being modified. Pages are added to the wr_buf kiobuf ++ * until this becomes full or the next page written to has a lower pagenr ++ * then the current max pagenr in the kiobuf. ++ */ ++static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, ++ size_t len, int *retlen) ++{ ++ int pagenr, offset; ++ size_t start_len = 0, end_len; ++ int pagecnt = 0; ++ struct kiobuf *iobuf = dev->wr_buf; ++ int err = 0; ++ struct page *pagelst[2]; ++ int pagenrs[2]; ++ int readpages = 0; ++ int ignorepage = -1; ++ ++ pagenr = to >> PAGE_SHIFT; ++ offset = to & ~PAGE_MASK; ++ ++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", ++ buf, (long)to, len, pagenr, offset); ++ ++ *retlen = 0; ++ /* see if we have to do a partial write at the start */ ++ if(offset) { ++ start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; ++ len -= start_len; ++ } ++ ++ /* calculate the length of the other two regions */ ++ end_len = len & ~PAGE_MASK; ++ len -= end_len; ++ ++ if(start_len) { ++ pagenrs[0] = pagenr; ++ readpages++; ++ pagecnt++; ++ } ++ if(len) ++ pagecnt += len >> PAGE_SHIFT; ++ if(end_len) { ++ pagenrs[readpages] = pagenr + pagecnt; ++ readpages++; ++ pagecnt++; ++ } ++ ++ DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", ++ start_len, len, end_len, pagecnt); ++ ++ down(&dev->wrbuf_mutex); ++ ++ if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1]) ++ || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) { ++ ++ if((pagenr == iobuf->blocks[iobuf->nr_pages-1]) ++ && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) { ++ iobuf->nr_pages--; ++ ignorepage = pagenr; ++ } else { ++ DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n", ++ pagenr, iobuf->blocks[iobuf->nr_pages-1], ++ pagecnt, iobuf->nr_pages); ++ commit_pages(dev); ++ } ++ } ++ ++ if(readpages) { ++ err = read_pages(dev, pagenrs, pagelst, readpages); ++ if(err < 0) ++ goto readin_err; ++ } ++ ++ if(start_len) { ++ /* do partial start region */ ++ struct page *page; ++ ++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", ++ pagenr, start_len, offset); ++ page = pagelst[0]; ++ BUG_ON(!buf); ++ if(PageDirty(page) && pagenr != ignorepage) { ++ err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", ++ to, start_len, len, end_len, pagenr, ignorepage); ++ BUG(); ++ } ++ memcpy(page_address(page)+offset, buf, start_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ buf += start_len; ++ *retlen = start_len; ++ err = 0; ++ iobuf->blocks[iobuf->nr_pages] = pagenr++; ++ iobuf->maplist[iobuf->nr_pages] = page; ++ iobuf->nr_pages++; ++ } ++ ++ /* Now do the main loop to a page aligned, n page sized output */ ++ if(len) { ++ int pagesc = len >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", ++ pagenr, pagesc); ++ while(pagesc) { ++ struct page *page; ++ ++ /* see if page is in the page cache */ ++ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); ++ page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenr); ++ if(PageDirty(page) && pagenr != ignorepage) { ++ BUG(); ++ } ++ if(!page) { ++ warn("write: cant grab cache page %d", pagenr); ++ err = -ENOMEM; ++ goto write_err; ++ } ++ if(!buf) { ++ memset(page_address(page), 0xff, PAGE_SIZE); ++ } else { ++ memcpy(page_address(page), buf, PAGE_SIZE); ++ buf += PAGE_SIZE; ++ } ++ iobuf->blocks[iobuf->nr_pages] = pagenr++; ++ iobuf->maplist[iobuf->nr_pages] = page; ++ iobuf->nr_pages++; ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ pagesc--; ++ *retlen += PAGE_SIZE; ++ } ++ } ++ ++ if(end_len) { ++ /* do the third region */ ++ struct page *page; ++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", ++ pagenr, end_len); ++ page = pagelst[readpages-1]; ++ BUG_ON(!buf); ++ if(PageDirty(page) && pagenr != ignorepage) { ++ err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", ++ to, start_len, len, end_len, pagenr, ignorepage); ++ BUG(); ++ } ++ memcpy(page_address(page), buf, end_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ DEBUG(3, "blkmtd: write: writing out partial end\n"); ++ *retlen += end_len; ++ err = 0; ++ iobuf->blocks[iobuf->nr_pages] = pagenr; ++ iobuf->maplist[iobuf->nr_pages] = page; ++ iobuf->nr_pages++; ++ } ++ ++ DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); ++ ++ if(sync) { ++write_err: ++ commit_pages(dev); ++ } ++ ++readin_err: ++ up(&dev->wrbuf_mutex); ++ return err; ++} ++ ++ ++/* erase a specified part of the device */ ++static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ struct mtd_erase_region_info *einfo = mtd->eraseregions; ++ int numregions = mtd->numeraseregions; ++ size_t from; ++ u_long len; ++ int err = -EIO; ++ size_t retlen; ++ ++ /* check readonly */ ++ if(!dev->wr_buf) { ++ err("error: mtd%d trying to erase readonly device %s", ++ mtd->index, mtd->name); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_callback; ++ } ++ ++ instr->state = MTD_ERASING; ++ from = instr->addr; ++ len = instr->len; ++ ++ /* check erase region has valid start and length */ ++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", ++ bdevname(dev->binding->bd_dev), from, len); ++ while(numregions) { ++ DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", ++ einfo->offset, einfo->erasesize, einfo->numblocks); ++ if(from >= einfo->offset ++ && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { ++ if(len == einfo->erasesize ++ && ( (from - einfo->offset) % einfo->erasesize == 0)) ++ break; ++ } ++ numregions--; ++ einfo++; ++ } ++ ++ if(!numregions) { ++ /* Not a valid erase block */ ++ err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); ++ instr->state = MTD_ERASE_FAILED; ++ err = -EIO; ++ } ++ ++ if(instr->state != MTD_ERASE_FAILED) { ++ /* do the erase */ ++ DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); ++ err = write_pages(dev, NULL, from, len, &retlen); ++ if(err < 0) { ++ err("erase failed err = %d", err); ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ err = 0; ++ } ++ } ++ ++ DEBUG(3, "blkmtd: erase: checking callback\n"); ++ erase_callback: ++ mtd_erase_callback(instr); ++ DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); ++ return err; ++} ++ ++ ++/* read a range of the data via the page cache */ ++static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err = 0; ++ int offset; ++ int pagenr, pages; ++ struct page **pagelst; ++ int *pagenrs; ++ int i; ++ ++ *retlen = 0; ++ ++ DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", ++ bdevname(dev->binding->bd_dev), from, len, buf); ++ ++ pagenr = from >> PAGE_SHIFT; ++ offset = from - (pagenr << PAGE_SHIFT); ++ ++ pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", ++ pagenr, offset, pages); ++ ++ pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL); ++ if(!pagelst) ++ return -ENOMEM; ++ pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL); ++ if(!pagenrs) { ++ kfree(pagelst); ++ return -ENOMEM; ++ } ++ for(i = 0; i < pages; i++) ++ pagenrs[i] = pagenr+i; ++ ++ err = read_pages(dev, pagenrs, pagelst, pages); ++ if(err) ++ goto readerr; ++ ++ pagenr = 0; ++ while(pages) { ++ struct page *page; ++ int cpylen; ++ ++ DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); ++ page = pagelst[pagenr]; ++ ++ cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; ++ if(offset+cpylen > PAGE_SIZE) ++ cpylen = PAGE_SIZE-offset; ++ ++ memcpy(buf + *retlen, page_address(page) + offset, cpylen); ++ offset = 0; ++ len -= cpylen; ++ *retlen += cpylen; ++ pagenr++; ++ pages--; ++ unlock_page(page); ++ if(!PageDirty(page)) ++ page_cache_release(page); ++ } ++ ++ readerr: ++ kfree(pagelst); ++ kfree(pagenrs); ++ DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", *retlen, err); ++ return err; ++} ++ ++ ++/* write data to the underlying device */ ++static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err; ++ ++ *retlen = 0; ++ if(!len) ++ return 0; ++ ++ DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", ++ bdevname(dev->binding->bd_dev), to, len, buf); ++ ++ /* handle readonly and out of range numbers */ ++ ++ if(!dev->wr_buf) { ++ err("error: trying to write to a readonly device %s", mtd->name); ++ return -EROFS; ++ } ++ ++ if(to >= mtd->size) { ++ return -ENOSPC; ++ } ++ ++ if(to + len > mtd->size) { ++ len = (mtd->size - to); ++ } ++ ++ err = write_pages(dev, buf, to, len, retlen); ++ if(err < 0) ++ *retlen = 0; ++ else ++ err = 0; ++ DEBUG(2, "blkmtd: write: end, err = %d\n", err); ++ return err; ++} ++ ++ ++/* sync the device - wait until the write queue is empty */ ++static void blkmtd_sync(struct mtd_info *mtd) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ struct kiobuf *iobuf = dev->wr_buf; ++ ++ DEBUG(2, "blkmtd: sync: called\n"); ++ if(iobuf == NULL) ++ return; ++ ++ DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", ++ iobuf->length, iobuf->nr_pages); ++ down(&dev->wrbuf_mutex); ++ if(iobuf->nr_pages) ++ commit_pages(dev); ++ up(&dev->wrbuf_mutex); ++} ++ ++ ++#ifdef BLKMTD_PROC_DEBUG ++/* procfs stuff */ ++static int blkmtd_proc_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len; ++ struct list_head *temp1, *temp2; ++ ++ MOD_INC_USE_COUNT; ++ ++ /* Count the size of the page lists */ ++ ++ len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); ++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { ++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, ++ list); ++ struct list_head *temp; ++ struct page *pagei; ++ ++ int clean = 0, dirty = 0, locked = 0, lru = 0; ++ /* Count the size of the page lists */ ++ list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { ++ pagei = list_entry(temp, struct page, list); ++ clean++; ++ if(PageLocked(pagei)) ++ locked++; ++ if(PageDirty(pagei)) ++ dirty++; ++ if(PageLRU(pagei)) ++ lru++; ++ } ++ list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { ++ pagei = list_entry(temp, struct page, list); ++ if(PageLocked(pagei)) ++ locked++; ++ if(PageDirty(pagei)) ++ dirty++; ++ if(PageLRU(pagei)) ++ lru++; ++ } ++ list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { ++ pagei = list_entry(temp, struct page, list); ++ if(PageLocked(pagei)) ++ locked++; ++ if(PageDirty(pagei)) ++ dirty++; ++ if(PageLRU(pagei)) ++ lru++; ++ } ++ ++ len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", ++ dev->mtd_info.index, ++ (dev->wr_buf && dev->wr_buf->nr_pages) ? ++ dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, ++ (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, ++ dev->binding->bd_inode->i_mapping->nrpages, ++ clean, dirty, locked, lru); ++ } ++ ++ if(len <= count) ++ *eof = 1; ++ ++ MOD_DEC_USE_COUNT; ++ return len; ++} ++#endif ++ ++ ++static void free_device(struct blkmtd_dev *dev) ++{ ++ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); ++ if(dev) { ++ del_mtd_device(&dev->mtd_info); ++ info("mtd%d: [%s] removed", dev->mtd_info.index, ++ dev->mtd_info.name + strlen("blkmtd: ")); ++ if(dev->mtd_info.eraseregions) ++ kfree(dev->mtd_info.eraseregions); ++ if(dev->mtd_info.name) ++ kfree(dev->mtd_info.name); ++ ++ if(dev->rd_buf) { ++ dev->rd_buf->locked = 0; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ if(dev->rd_buf->blocks) ++ kfree(dev->rd_buf->blocks); ++#endif ++ free_kiovec(1, &dev->rd_buf); ++ } ++ if(dev->wr_buf) { ++ dev->wr_buf->locked = 0; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ if(dev->wr_buf->blocks) ++ kfree(dev->rw_buf->blocks); ++#endif ++ free_kiovec(1, &dev->wr_buf); ++ } ++ ++ if(dev->binding) { ++ kdev_t kdev = to_kdev_t(dev->binding->bd_dev); ++ invalidate_inode_pages(dev->binding->bd_inode); ++ set_blocksize(kdev, 1 << 10); ++ blkdev_put(dev->binding, BDEV_RAW); ++ } ++ kfree(dev); ++ } ++} ++ ++ ++/* For a given size and initial erase size, calculate the number ++ * and size of each erase region. Goes round the loop twice, ++ * once to find out how many regions, then allocates space, ++ * then round the loop again to fill it in. ++ */ ++static struct mtd_erase_region_info *calc_erase_regions( ++ size_t erase_size, size_t total_size, int *regions) ++{ ++ struct mtd_erase_region_info *info = NULL; ++ ++ DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", ++ erase_size, total_size, *regions); ++ /* Make any user specified erasesize be a power of 2 ++ and at least PAGE_SIZE */ ++ if(erase_size) { ++ int es = erase_size; ++ erase_size = 1; ++ while(es != 1) { ++ es >>= 1; ++ erase_size <<= 1; ++ } ++ if(erase_size < PAGE_SIZE) ++ erase_size = PAGE_SIZE; ++ } else { ++ erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; ++ } ++ ++ *regions = 0; ++ ++ do { ++ int tot_size = total_size; ++ int er_size = erase_size; ++ int count = 0, offset = 0, regcnt = 0; ++ ++ while(tot_size) { ++ count = tot_size / er_size; ++ if(count) { ++ tot_size = tot_size % er_size; ++ if(info) { ++ DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", ++ offset, er_size, count); ++ (info+regcnt)->offset = offset; ++ (info+regcnt)->erasesize = er_size; ++ (info+regcnt)->numblocks = count; ++ (*regions)++; ++ } ++ regcnt++; ++ offset += (count * er_size); ++ } ++ while(er_size > tot_size) ++ er_size >>= 1; ++ } ++ if(info == NULL) { ++ info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if(!info) ++ break; ++ } ++ } while(!(*regions)); ++ DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", ++ erase_size, total_size, *regions); ++ return info; ++} ++ ++ ++extern kdev_t name_to_kdev_t(char *line) __init; ++ ++ ++static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) ++{ ++ int maj, min; ++ kdev_t kdev; ++ int mode; ++ struct blkmtd_dev *dev; ++ ++#ifdef MODULE ++ struct file *file = NULL; ++ struct inode *inode; ++#endif ++ ++ if(!devname) ++ return NULL; ++ ++ /* Get a handle on the device */ ++ mode = (readonly) ? O_RDONLY : O_RDWR; ++ ++#ifdef MODULE ++ ++ file = filp_open(devname, mode, 0); ++ if(IS_ERR(file)) { ++ err("error: cant open device %s", devname); ++ DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); ++ return NULL; ++ } ++ ++ /* determine is this is a block device and ++ * if so get its major and minor numbers ++ */ ++ inode = file->f_dentry->d_inode; ++ if(!S_ISBLK(inode->i_mode)) { ++ err("%s not a block device", devname); ++ filp_close(file, NULL); ++ return NULL; ++ } ++ kdev = inode->i_rdev; ++ filp_close(file, NULL); ++#else ++ kdev = name_to_kdev_t(devname); ++#endif /* MODULE */ ++ ++ if(!kdev) { ++ err("bad block device: `%s'", devname); ++ return NULL; ++ } ++ ++ maj = MAJOR(kdev); ++ min = MINOR(kdev); ++ DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", ++ maj, min); ++ ++ if(maj == MTD_BLOCK_MAJOR) { ++ err("attempting to use an MTD device as a block device"); ++ return NULL; ++ } ++ ++ DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); ++ ++ dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); ++ if(dev == NULL) ++ return NULL; ++ ++ memset(dev, 0, sizeof(struct blkmtd_dev)); ++ if(alloc_kiovec(1, &dev->rd_buf)) { ++ err("cant allocate read iobuf"); ++ goto devinit_err; ++ } ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); ++ if(dev->rd_buf->blocks == NULL) { ++ crit("cant allocate rd_buf blocks"); ++ goto devinit_err; ++ } ++#endif ++ ++ if(!readonly) { ++ if(alloc_kiovec(1, &dev->wr_buf)) { ++ err("cant allocate kiobuf - readonly enabled"); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) ++ } else { ++ dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); ++ if(dev->wr_buf->blocks == NULL) { ++ crit("cant allocate wr_buf blocks - readonly enabled"); ++ free_kiovec(1, &iobuf); ++ } ++#endif ++ } ++ if(dev->wr_buf) ++ init_MUTEX(&dev->wrbuf_mutex); ++ } ++ ++ /* get the block device */ ++ dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); ++ if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) ++ goto devinit_err; ++ ++ if(set_blocksize(kdev, PAGE_SIZE)) { ++ err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); ++ goto devinit_err; ++ } ++ ++ dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; ++ ++ /* Setup the MTD structure */ ++ /* make the name contain the block device in */ ++ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); ++ if(dev->mtd_info.name == NULL) ++ goto devinit_err; ++ ++ sprintf(dev->mtd_info.name, "blkmtd: %s", devname); ++ dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, ++ &dev->mtd_info.numeraseregions); ++ if(dev->mtd_info.eraseregions == NULL) ++ goto devinit_err; ++ ++ dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; ++ DEBUG(1, "blkmtd: init: found %d erase regions\n", ++ dev->mtd_info.numeraseregions); ++ ++ if(readonly) { ++ dev->mtd_info.type = MTD_ROM; ++ dev->mtd_info.flags = MTD_CAP_ROM; ++ } else { ++ dev->mtd_info.type = MTD_RAM; ++ dev->mtd_info.flags = MTD_CAP_RAM; ++ } ++ dev->mtd_info.erase = blkmtd_erase; ++ dev->mtd_info.read = blkmtd_read; ++ dev->mtd_info.write = blkmtd_write; ++ dev->mtd_info.sync = blkmtd_sync; ++ dev->mtd_info.point = 0; ++ dev->mtd_info.unpoint = 0; ++ dev->mtd_info.priv = dev; ++ dev->mtd_info.owner = THIS_MODULE; ++ ++ list_add(&dev->list, &blkmtd_device_list); ++ if (add_mtd_device(&dev->mtd_info)) { ++ /* Device didnt get added, so free the entry */ ++ list_del(&dev->list); ++ free_device(dev); ++ return NULL; ++ } else { ++ info("mtd%d: [%s] erase_size = %dKiB %s", ++ dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), ++ dev->mtd_info.erasesize >> 10, ++ (dev->wr_buf) ? "" : "(read-only)"); ++ } ++ ++ return dev; ++ ++ devinit_err: ++ free_device(dev); ++ return NULL; ++} ++ ++ ++/* Cleanup and exit - sync the device and kill of the kernel thread */ ++static void __devexit cleanup_blkmtd(void) ++{ ++ struct list_head *temp1, *temp2; ++#ifdef BLKMTD_PROC_DEBUG ++ if(blkmtd_proc) { ++ remove_proc_entry("blkmtd_debug", NULL); ++ } ++#endif ++ ++ /* Remove the MTD devices */ ++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { ++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, ++ list); ++ blkmtd_sync(&dev->mtd_info); ++ free_device(dev); ++ } ++} ++ ++#ifndef MODULE ++ ++/* Handle kernel boot params */ ++ ++ ++static int __init param_blkmtd_device(char *str) ++{ ++ int i; ++ ++ for(i = 0; i < MAX_DEVICES; i++) { ++ device[i] = str; ++ DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); ++ strsep(&str, ","); ++ } ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_erasesz(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ erasesz[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_ro(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ ro[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_sync(char *str) ++{ ++ if(str[0] == '1') ++ sync = 1; ++ return 1; ++} ++ ++__setup("blkmtd_device=", param_blkmtd_device); ++__setup("blkmtd_erasesz=", param_blkmtd_erasesz); ++__setup("blkmtd_ro=", param_blkmtd_ro); ++__setup("blkmtd_sync=", param_blkmtd_sync); ++ ++#endif ++ ++ ++/* Startup */ ++static int __init init_blkmtd(void) ++{ ++ int i; ++ ++ /* Check args - device[0] is the bare minimum*/ ++ if(!device[0]) { ++ err("error: missing `device' name\n"); ++ return -EINVAL; ++ } ++ ++ for(i = 0; i < MAX_DEVICES; i++) ++ add_device(device[i], ro[i], erasesz[i] << 10); ++ ++ if(list_empty(&blkmtd_device_list)) ++ goto init_err; ++ ++ info("version " VERSION); ++ ++#ifdef BLKMTD_PROC_DEBUG ++ /* create proc entry */ ++ DEBUG(2, "Creating /proc/blkmtd_debug\n"); ++ blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, ++ NULL, blkmtd_proc_read, NULL); ++ if(blkmtd_proc == NULL) { ++ err("Cant create /proc/blkmtd_debug"); ++ } else { ++ blkmtd_proc->owner = THIS_MODULE; ++ } ++#endif ++ ++ if(!list_empty(&blkmtd_device_list)) ++ /* Everything is ok if we got here */ ++ return 0; ++ ++ init_err: ++ return -EINVAL; ++} ++ ++module_init(init_blkmtd); ++module_exit(cleanup_blkmtd); +--- linux-2.4.21/drivers/mtd/devices/blkmtd.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/blkmtd.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * blkmtd.c - use a block device as a fake MTD + * +@@ -12,11 +12,8 @@ + * How it works: + * The driver uses raw/io to read/write the device and the page + * cache to cache access. Writes update the page cache with the +- * new data and mark it dirty and add the page into a kiobuf. +- * When the kiobuf becomes full or the next extry is to an earlier +- * block in the kiobuf then it is flushed to disk. This allows +- * writes to remained ordered and gives a small and simple outgoing +- * write cache. ++ * new data and mark it dirty and add the page into a BIO which ++ * is then written out. + * + * It can be loaded Read-Only to prevent erases and writes to the + * medium. +@@ -27,20 +24,12 @@ + #include + #include + #include +-#include +-#include ++#include + #include + #include ++#include + #include + +-#ifdef CONFIG_MTD_DEBUG +-#ifdef CONFIG_PROC_FS +-# include +-# define BLKMTD_PROC_DEBUG +- static struct proc_dir_entry *blkmtd_proc; +-#endif +-#endif +- + + #define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) + #define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) +@@ -48,17 +37,15 @@ + #define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) + + +-/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */ ++/* Default erase size in K, always make it a multiple of PAGE_SIZE */ + #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ +-#define VERSION "1.10" ++#define VERSION "$Revision$" + + /* Info for the block device */ + struct blkmtd_dev { + struct list_head list; +- struct block_device *binding; ++ struct block_device *blkdev; + struct mtd_info mtd_info; +- struct kiobuf *rd_buf, *wr_buf; +- long iobuf_locks; + struct semaphore wrbuf_mutex; + }; + +@@ -72,10 +59,10 @@ + #define MAX_DEVICES 4 + + /* Module parameters passed by insmod/modprobe */ +-char *device[MAX_DEVICES]; /* the block device to use */ +-int erasesz[MAX_DEVICES]; /* optional default erase size */ +-int ro[MAX_DEVICES]; /* optional read only flag */ +-int sync; ++static char *device[MAX_DEVICES]; /* the block device to use */ ++static int erasesz[MAX_DEVICES]; /* optional default erase size */ ++static int ro[MAX_DEVICES]; /* optional read only flag */ ++static int sync; + + + MODULE_LICENSE("GPL"); +@@ -91,136 +78,145 @@ + MODULE_PARM_DESC(sync, "1=Synchronous writes"); + + +-/** +- * read_pages - read in pages via the page cache +- * @dev: device to read from +- * @pagenrs: list of page numbers wanted +- * @pagelst: storage for struce page * pointers +- * @pages: count of pages wanted +- * +- * Read pages, getting them from the page cache if available +- * else reading them in from disk if not. pagelst must be preallocated +- * to hold the page count. +- */ +-static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages) ++/* completion handler for BIO reads */ ++static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) + { +- kdev_t kdev; +- struct page *page; +- int cnt = 0; +- struct kiobuf *iobuf; +- int err = 0; ++ if (bio->bi_size) ++ return 1; + +- if(!dev) { +- err("read_pages: PANIC dev == NULL"); +- return -EIO; +- } +- kdev = to_kdev_t(dev->binding->bd_dev); ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} + +- DEBUG(2, "read_pages: reading %d pages\n", pages); +- if(test_and_set_bit(0, &dev->iobuf_locks)) { +- err = alloc_kiovec(1, &iobuf); +- if (err) { +- crit("cant allocate kiobuf"); +- return -ENOMEM; +- } +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); +- if(iobuf->blocks == NULL) { +- crit("cant allocate iobuf blocks"); +- free_kiovec(1, &iobuf); +- return -ENOMEM; +- } +-#endif ++ ++/* completion handler for BIO writes */ ++static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) ++{ ++ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); ++ struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; ++ ++ if (bio->bi_size) ++ return 1; ++ ++ if(!uptodate) ++ err("bi_write_complete: not uptodate\n"); ++ ++ do { ++ struct page *page = bvec->bv_page; ++ DEBUG(3, "Cleaning up page %ld\n", page->index); ++ if (--bvec >= bio->bi_io_vec) ++ prefetchw(&bvec->bv_page->flags); ++ ++ if (uptodate) { ++ SetPageUptodate(page); + } else { +- iobuf = dev->rd_buf; ++ ClearPageUptodate(page); ++ SetPageError(page); + } ++ ClearPageDirty(page); ++ unlock_page(page); ++ page_cache_release(page); ++ } while (bvec >= bio->bi_io_vec); + +- iobuf->nr_pages = 0; +- iobuf->length = 0; +- iobuf->offset = 0; +- iobuf->locked = 1; ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} + +- for(cnt = 0; cnt < pages; cnt++) { +- page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); +- pagelst[cnt] = page; +- if(!PageUptodate(page)) { +- iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; +- iobuf->maplist[iobuf->nr_pages++] = page; +- } +- } + +- if(iobuf->nr_pages) { +- iobuf->length = iobuf->nr_pages << PAGE_SHIFT; +- err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); +- DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err); +- if(err < 0) { +- while(pages--) { +- ClearPageUptodate(pagelst[pages]); +- unlock_page(pagelst[pages]); +- page_cache_release(pagelst[pages]); +- } +- } else { +- while(iobuf->nr_pages--) { +- SetPageUptodate(iobuf->maplist[iobuf->nr_pages]); ++/* read one page from the block device */ ++static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) ++{ ++ struct bio *bio; ++ struct completion event; ++ int err = -ENOMEM; ++ ++ if(PageUptodate(page)) { ++ DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); ++ unlock_page(page); ++ return 0; + } +- err = 0; ++ ++ ClearPageUptodate(page); ++ ClearPageError(page); ++ ++ bio = bio_alloc(GFP_KERNEL, 1); ++ if(bio) { ++ init_completion(&event); ++ bio->bi_bdev = dev->blkdev; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_read_complete; ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { ++ submit_bio(READ_SYNC, bio); ++ wait_for_completion(&event); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); + } + } + ++ if(err) ++ SetPageError(page); ++ else ++ SetPageUptodate(page); ++ flush_dcache_page(page); ++ unlock_page(page); ++ return err; ++} + +- if(iobuf != dev->rd_buf) { +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- kfree(iobuf->blocks); +-#endif +- free_kiovec(1, &iobuf); +- } else { +- clear_bit(0, &dev->iobuf_locks); ++ ++/* write out the current BIO and wait for it to finish */ ++static int blkmtd_write_out(struct bio *bio) ++{ ++ struct completion event; ++ int err; ++ ++ if(!bio->bi_vcnt) { ++ bio_put(bio); ++ return 0; + } +- DEBUG(2, "read_pages: done, err = %d\n", err); ++ ++ init_completion(&event); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_write_complete; ++ submit_bio(WRITE_SYNC, bio); ++ wait_for_completion(&event); ++ DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); + return err; + } + + + /** +- * commit_pages - commit pages in the writeout kiobuf to disk +- * @dev: device to write to ++ * blkmtd_add_page - add a page to the current BIO ++ * @bio: bio to add to (NULL to alloc initial bio) ++ * @blkdev: block device ++ * @page: page to add ++ * @pagecnt: pages left to add + * +- * If the current dev has pages in the dev->wr_buf kiobuf, +- * they are written to disk using brw_kiovec() ++ * Adds a page to the current bio, allocating it if necessary. If it cannot be ++ * added, the current bio is written out and a new one is allocated. Returns ++ * the new bio to add or NULL on error + */ +-static int commit_pages(struct blkmtd_dev *dev) ++static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, ++ struct page *page, int pagecnt) + { +- struct kiobuf *iobuf = dev->wr_buf; +- kdev_t kdev = to_kdev_t(dev->binding->bd_dev); +- int err = 0; + +- iobuf->length = iobuf->nr_pages << PAGE_SHIFT; +- iobuf->locked = 1; +- if(iobuf->length) { +- int i; +- DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages); +- /* Check all the pages are dirty and lock them */ +- for(i = 0; i < iobuf->nr_pages; i++) { +- struct page *page = iobuf->maplist[i]; +- BUG_ON(!PageDirty(page)); +- lock_page(page); +- } +- err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); +- DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err); +- while(iobuf->nr_pages) { +- struct page *page = iobuf->maplist[--iobuf->nr_pages]; +- ClearPageDirty(page); +- SetPageUptodate(page); +- unlock_page(page); +- page_cache_release(page); +- } ++ retry: ++ if(!bio) { ++ bio = bio_alloc(GFP_KERNEL, pagecnt); ++ if(!bio) ++ return NULL; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_bdev = blkdev; + } + +- DEBUG(2, "blkmtd: sync: end, err = %d\n", err); +- iobuf->offset = 0; +- iobuf->nr_pages = 0; +- iobuf->length = 0; +- return err; ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { ++ blkmtd_write_out(bio); ++ bio = NULL; ++ goto retry; ++ } ++ return bio; + } + + +@@ -234,30 +230,25 @@ + * + * Grab pages from the page cache and fill them with the source data. + * Non page aligned start and end result in a readin of the page and +- * part of the page being modified. Pages are added to the wr_buf kiobuf +- * until this becomes full or the next page written to has a lower pagenr +- * then the current max pagenr in the kiobuf. ++ * part of the page being modified. Pages are added to the bio and then written ++ * out. + */ + static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, +- size_t len, int *retlen) ++ size_t len, size_t *retlen) + { + int pagenr, offset; + size_t start_len = 0, end_len; + int pagecnt = 0; +- struct kiobuf *iobuf = dev->wr_buf; + int err = 0; +- struct page *pagelst[2]; +- int pagenrs[2]; +- int readpages = 0; +- int ignorepage = -1; ++ struct bio *bio = NULL; ++ size_t thislen = 0; + + pagenr = to >> PAGE_SHIFT; + offset = to & ~PAGE_MASK; + +- DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n", ++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", + buf, (long)to, len, pagenr, offset); + +- *retlen = 0; + /* see if we have to do a partial write at the start */ + if(offset) { + start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; +@@ -268,68 +259,48 @@ + end_len = len & ~PAGE_MASK; + len -= end_len; + +- if(start_len) { +- pagenrs[0] = pagenr; +- readpages++; ++ if(start_len) + pagecnt++; +- } ++ + if(len) + pagecnt += len >> PAGE_SHIFT; +- if(end_len) { +- pagenrs[readpages] = pagenr + pagecnt; +- readpages++; +- pagecnt++; +- } + +- DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n", +- start_len, len, end_len, pagecnt); ++ if(end_len) ++ pagecnt++; + + down(&dev->wrbuf_mutex); + +- if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1]) +- || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) { +- +- if((pagenr == iobuf->blocks[iobuf->nr_pages-1]) +- && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) { +- iobuf->nr_pages--; +- ignorepage = pagenr; +- } else { +- DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n", +- pagenr, iobuf->blocks[iobuf->nr_pages-1], +- pagecnt, iobuf->nr_pages); +- commit_pages(dev); +- } +- } +- +- if(readpages) { +- err = read_pages(dev, pagenrs, pagelst, readpages); +- if(err < 0) +- goto readin_err; +- } ++ DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", ++ start_len, len, end_len, pagecnt); + + if(start_len) { + /* do partial start region */ + struct page *page; + +- DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", ++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", + pagenr, start_len, offset); +- page = pagelst[0]; ++ + BUG_ON(!buf); +- if(PageDirty(page) && pagenr != ignorepage) { +- err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n", +- to, start_len, len, end_len, pagenr, ignorepage); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); + BUG(); + } + memcpy(page_address(page)+offset, buf, start_len); + SetPageDirty(page); + SetPageUptodate(page); +- unlock_page(page); + buf += start_len; +- *retlen = start_len; +- err = 0; +- iobuf->blocks[iobuf->nr_pages] = pagenr++; +- iobuf->maplist[iobuf->nr_pages] = page; +- iobuf->nr_pages++; ++ thislen = start_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagecnt--; ++ pagenr++; + } + + /* Now do the main loop to a page aligned, n page sized output */ +@@ -342,12 +313,12 @@ + + /* see if page is in the page cache */ + DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); +- page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenr); +- if(PageDirty(page) && pagenr != ignorepage) { ++ page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); ++ if(PageDirty(page)) { + BUG(); + } + if(!page) { +- warn("write: cant grab cache page %d", pagenr); ++ warn("write: cannot grab cache page %d", pagenr); + err = -ENOMEM; + goto write_err; + } +@@ -357,50 +328,58 @@ + memcpy(page_address(page), buf, PAGE_SIZE); + buf += PAGE_SIZE; + } +- iobuf->blocks[iobuf->nr_pages] = pagenr++; +- iobuf->maplist[iobuf->nr_pages] = page; +- iobuf->nr_pages++; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagenr++; ++ pagecnt--; + SetPageDirty(page); + SetPageUptodate(page); +- unlock_page(page); + pagesc--; +- *retlen += PAGE_SIZE; ++ thislen += PAGE_SIZE; + } + } + + if(end_len) { + /* do the third region */ + struct page *page; +- DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", ++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", + pagenr, end_len); +- page = pagelst[readpages-1]; + BUG_ON(!buf); +- if(PageDirty(page) && pagenr != ignorepage) { +- err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n", +- to, start_len, len, end_len, pagenr, ignorepage); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); + BUG(); + } + memcpy(page_address(page), buf, end_len); + SetPageDirty(page); + SetPageUptodate(page); +- unlock_page(page); + DEBUG(3, "blkmtd: write: writing out partial end\n"); +- *retlen += end_len; +- err = 0; +- iobuf->blocks[iobuf->nr_pages] = pagenr; +- iobuf->maplist[iobuf->nr_pages] = page; +- iobuf->nr_pages++; ++ thislen += end_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; + } +- +- DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err); +- +- if(sync) { +-write_err: +- commit_pages(dev); ++ pagenr++; + } + +-readin_err: ++ DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); ++ write_err: ++ if(bio) ++ blkmtd_write_out(bio); ++ ++ DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); + up(&dev->wrbuf_mutex); ++ ++ if(retlen) ++ *retlen = thislen; + return err; + } + +@@ -414,23 +393,15 @@ + size_t from; + u_long len; + int err = -EIO; +- int retlen; +- +- /* check readonly */ +- if(!dev->wr_buf) { +- err("error: mtd%d trying to erase readonly device %s", +- mtd->index, mtd->name); +- instr->state = MTD_ERASE_FAILED; +- goto erase_callback; +- } ++ size_t retlen; + + instr->state = MTD_ERASING; + from = instr->addr; + len = instr->len; + + /* check erase region has valid start and length */ +- DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n", +- bdevname(dev->binding->bd_dev), from, len); ++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", ++ mtd->name+9, from, len); + while(numregions) { + DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", + einfo->offset, einfo->erasesize, einfo->numblocks); +@@ -446,29 +417,25 @@ + + if(!numregions) { + /* Not a valid erase block */ +- err("erase: invalid erase request 0x%lX @ 0x%08X", len, from); ++ err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); + instr->state = MTD_ERASE_FAILED; + err = -EIO; + } + + if(instr->state != MTD_ERASE_FAILED) { + /* do the erase */ +- DEBUG(3, "Doing erase from = %d len = %ld\n", from, len); ++ DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); + err = write_pages(dev, NULL, from, len, &retlen); +- if(err < 0) { ++ if(err || retlen != len) { + err("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; +- err = 0; + } + } + + DEBUG(3, "blkmtd: erase: checking callback\n"); +- erase_callback: +- if (instr->callback) { +- (*(instr->callback))(instr); +- } ++ mtd_erase_callback(instr); + DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); + return err; + } +@@ -482,14 +449,15 @@ + int err = 0; + int offset; + int pagenr, pages; +- struct page **pagelst; +- int *pagenrs; +- int i; ++ size_t thislen = 0; + +- *retlen = 0; ++ DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", ++ mtd->name+9, from, len, buf); + +- DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n", +- bdevname(dev->binding->bd_dev), (long int)from, len, buf); ++ if(from > mtd->size) ++ return -EINVAL; ++ if(from + len > mtd->size) ++ len = mtd->size - from; + + pagenr = from >> PAGE_SHIFT; + offset = from - (pagenr << PAGE_SHIFT); +@@ -498,48 +466,35 @@ + DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", + pagenr, offset, pages); + +- pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL); +- if(!pagelst) +- return -ENOMEM; +- pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL); +- if(!pagenrs) { +- kfree(pagelst); +- return -ENOMEM; +- } +- for(i = 0; i < pages; i++) +- pagenrs[i] = pagenr+i; +- +- err = read_pages(dev, pagenrs, pagelst, pages); +- if(err) +- goto readerr; +- +- pagenr = 0; + while(pages) { + struct page *page; + int cpylen; + + DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); +- page = pagelst[pagenr]; ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ if(IS_ERR(page)) { ++ err = -EIO; ++ goto readerr; ++ } + + cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; + if(offset+cpylen > PAGE_SIZE) + cpylen = PAGE_SIZE-offset; + +- memcpy(buf + *retlen, page_address(page) + offset, cpylen); ++ memcpy(buf + thislen, page_address(page) + offset, cpylen); + offset = 0; + len -= cpylen; +- *retlen += cpylen; ++ thislen += cpylen; + pagenr++; + pages--; +- unlock_page(page); + if(!PageDirty(page)) + page_cache_release(page); + } + + readerr: +- kfree(pagelst); +- kfree(pagenrs); +- DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", *retlen, err); ++ if(retlen) ++ *retlen = thislen; ++ DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err); + return err; + } + +@@ -551,32 +506,22 @@ + struct blkmtd_dev *dev = mtd->priv; + int err; + +- *retlen = 0; + if(!len) + return 0; + +- DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n", +- bdevname(dev->binding->bd_dev), (long int)to, len, buf); +- +- /* handle readonly and out of range numbers */ +- +- if(!dev->wr_buf) { +- err("error: trying to write to a readonly device %s", mtd->name); +- return -EROFS; +- } ++ DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", ++ mtd->name+9, to, len, buf); + + if(to >= mtd->size) { + return -ENOSPC; + } + + if(to + len > mtd->size) { +- len = (mtd->size - to); ++ len = mtd->size - to; + } + + err = write_pages(dev, buf, to, len, retlen); +- if(err < 0) +- *retlen = 0; +- else ++ if(err > 0) + err = 0; + DEBUG(2, "blkmtd: write: end, err = %d\n", err); + return err; +@@ -586,124 +531,22 @@ + /* sync the device - wait until the write queue is empty */ + static void blkmtd_sync(struct mtd_info *mtd) + { +- struct blkmtd_dev *dev = mtd->priv; +- struct kiobuf *iobuf = dev->wr_buf; +- +- DEBUG(2, "blkmtd: sync: called\n"); +- if(iobuf == NULL) +- return; +- +- DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", +- iobuf->length, iobuf->nr_pages); +- down(&dev->wrbuf_mutex); +- if(iobuf->nr_pages) +- commit_pages(dev); +- up(&dev->wrbuf_mutex); +-} +- +- +-#ifdef BLKMTD_PROC_DEBUG +-/* procfs stuff */ +-static int blkmtd_proc_read(char *page, char **start, off_t off, +- int count, int *eof, void *data) +-{ +- int len; +- struct list_head *temp1, *temp2; +- +- MOD_INC_USE_COUNT; +- +- /* Count the size of the page lists */ +- +- len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); +- list_for_each_safe(temp1, temp2, &blkmtd_device_list) { +- struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, +- list); +- struct list_head *temp; +- struct page *pagei; +- +- int clean = 0, dirty = 0, locked = 0, lru = 0; +- /* Count the size of the page lists */ +- list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { +- pagei = list_entry(temp, struct page, list); +- clean++; +- if(PageLocked(pagei)) +- locked++; +- if(PageDirty(pagei)) +- dirty++; +- if(PageLRU(pagei)) +- lru++; +- } +- list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { +- pagei = list_entry(temp, struct page, list); +- if(PageLocked(pagei)) +- locked++; +- if(PageDirty(pagei)) +- dirty++; +- if(PageLRU(pagei)) +- lru++; +- } +- list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { +- pagei = list_entry(temp, struct page, list); +- if(PageLocked(pagei)) +- locked++; +- if(PageDirty(pagei)) +- dirty++; +- if(PageLRU(pagei)) +- lru++; +- } +- +- len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", +- dev->mtd_info.index, +- (dev->wr_buf && dev->wr_buf->nr_pages) ? +- dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, +- (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, +- dev->binding->bd_inode->i_mapping->nrpages, +- clean, dirty, locked, lru); +- } +- +- if(len <= count) +- *eof = 1; +- +- MOD_DEC_USE_COUNT; +- return len; ++ /* Currently all writes are synchronous */ + } +-#endif + + + static void free_device(struct blkmtd_dev *dev) + { + DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); + if(dev) { +- del_mtd_device(&dev->mtd_info); +- info("mtd%d: [%s] removed", dev->mtd_info.index, +- dev->mtd_info.name + strlen("blkmtd: ")); + if(dev->mtd_info.eraseregions) + kfree(dev->mtd_info.eraseregions); + if(dev->mtd_info.name) + kfree(dev->mtd_info.name); + +- if(dev->rd_buf) { +- dev->rd_buf->locked = 0; +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- if(dev->rd_buf->blocks) +- kfree(dev->rd_buf->blocks); +-#endif +- free_kiovec(1, &dev->rd_buf); +- } +- if(dev->wr_buf) { +- dev->wr_buf->locked = 0; +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- if(dev->wr_buf->blocks) +- kfree(dev->rw_buf->blocks); +-#endif +- free_kiovec(1, &dev->wr_buf); +- } +- +- if(dev->binding) { +- kdev_t kdev = to_kdev_t(dev->binding->bd_dev); +- invalidate_inode_pages(dev->binding->bd_inode); +- set_blocksize(kdev, 1 << 10); +- blkdev_put(dev->binding, BDEV_RAW); ++ if(dev->blkdev) { ++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); ++ close_bdev_excl(dev->blkdev); + } + kfree(dev); + } +@@ -720,7 +563,7 @@ + { + struct mtd_erase_region_info *info = NULL; + +- DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n", ++ DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + /* Make any user specified erasesize be a power of 2 + and at least PAGE_SIZE */ +@@ -768,119 +611,62 @@ + break; + } + } while(!(*regions)); +- DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n", ++ DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + return info; + } + + +-extern kdev_t name_to_kdev_t(char *line) __init; +- ++extern dev_t __init name_to_dev_t(const char *line); + + static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) + { +- int maj, min; +- kdev_t kdev; ++ struct block_device *bdev; + int mode; + struct blkmtd_dev *dev; + +-#ifdef MODULE +- struct file *file = NULL; +- struct inode *inode; +-#endif +- + if(!devname) + return NULL; + + /* Get a handle on the device */ +- mode = (readonly) ? O_RDONLY : O_RDWR; + +-#ifdef MODULE + +- file = filp_open(devname, mode, 0); +- if(IS_ERR(file)) { +- err("error: cant open device %s", devname); +- DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); +- return NULL; +- } +- +- /* determine is this is a block device and +- * if so get its major and minor numbers +- */ +- inode = file->f_dentry->d_inode; +- if(!S_ISBLK(inode->i_mode)) { +- err("%s not a block device", devname); +- filp_close(file, NULL); +- return NULL; +- } +- kdev = inode->i_rdev; +- filp_close(file, NULL); ++#ifdef MODULE ++ mode = (readonly) ? O_RDONLY : O_RDWR; ++ bdev = open_bdev_excl(devname, mode, NULL); + #else +- kdev = name_to_kdev_t(devname); +-#endif /* MODULE */ +- +- if(!kdev) { +- err("bad block device: `%s'", devname); ++ mode = (readonly) ? FMODE_READ : FMODE_WRITE; ++ bdev = open_by_devnum(name_to_dev_t(devname), mode); ++#endif ++ if(IS_ERR(bdev)) { ++ err("error: cannot open device %s", devname); ++ DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); + return NULL; + } + +- maj = MAJOR(kdev); +- min = MINOR(kdev); + DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", +- maj, min); ++ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); + +- if(maj == MTD_BLOCK_MAJOR) { ++ if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + err("attempting to use an MTD device as a block device"); ++ blkdev_put(bdev); + return NULL; + } + +- DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); +- + dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); +- if(dev == NULL) ++ if(dev == NULL) { ++ blkdev_put(bdev); + return NULL; +- +- memset(dev, 0, sizeof(struct blkmtd_dev)); +- if(alloc_kiovec(1, &dev->rd_buf)) { +- err("cant allocate read iobuf"); +- goto devinit_err; + } +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); +- if(dev->rd_buf->blocks == NULL) { +- crit("cant allocate rd_buf blocks"); +- goto devinit_err; +- } +-#endif + ++ memset(dev, 0, sizeof(struct blkmtd_dev)); ++ dev->blkdev = bdev; ++ atomic_set(&(dev->blkdev->bd_inode->i_mapping->truncate_count), 0); + if(!readonly) { +- if(alloc_kiovec(1, &dev->wr_buf)) { +- err("cant allocate kiobuf - readonly enabled"); +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +- } else { +- dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); +- if(dev->wr_buf->blocks == NULL) { +- crit("cant allocate wr_buf blocks - readonly enabled"); +- free_kiovec(1, &iobuf); +- } +-#endif +- } +- if(dev->wr_buf) + init_MUTEX(&dev->wrbuf_mutex); + } + +- /* get the block device */ +- dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); +- if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) +- goto devinit_err; +- +- if(set_blocksize(kdev, PAGE_SIZE)) { +- err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); +- goto devinit_err; +- } +- +- dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; ++ dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; + + /* Setup the MTD structure */ + /* make the name contain the block device in */ +@@ -904,27 +690,26 @@ + } else { + dev->mtd_info.type = MTD_RAM; + dev->mtd_info.flags = MTD_CAP_RAM; +- } + dev->mtd_info.erase = blkmtd_erase; +- dev->mtd_info.read = blkmtd_read; + dev->mtd_info.write = blkmtd_write; ++ dev->mtd_info.writev = default_mtd_writev; + dev->mtd_info.sync = blkmtd_sync; +- dev->mtd_info.point = 0; +- dev->mtd_info.unpoint = 0; ++ } ++ dev->mtd_info.read = blkmtd_read; ++ dev->mtd_info.readv = default_mtd_readv; + dev->mtd_info.priv = dev; +- dev->mtd_info.module = THIS_MODULE; ++ dev->mtd_info.owner = THIS_MODULE; + + list_add(&dev->list, &blkmtd_device_list); + if (add_mtd_device(&dev->mtd_info)) { + /* Device didnt get added, so free the entry */ + list_del(&dev->list); +- free_device(dev); +- return NULL; ++ goto devinit_err; + } else { + info("mtd%d: [%s] erase_size = %dKiB %s", + dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), + dev->mtd_info.erasesize >> 10, +- (dev->wr_buf) ? "" : "(read-only)"); ++ readonly ? "(read-only)" : ""); + } + + return dev; +@@ -939,17 +724,16 @@ + static void __devexit cleanup_blkmtd(void) + { + struct list_head *temp1, *temp2; +-#ifdef BLKMTD_PROC_DEBUG +- if(blkmtd_proc) { +- remove_proc_entry("blkmtd_debug", NULL); +- } +-#endif + + /* Remove the MTD devices */ + list_for_each_safe(temp1, temp2, &blkmtd_device_list) { + struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, + list); + blkmtd_sync(&dev->mtd_info); ++ del_mtd_device(&dev->mtd_info); ++ info("mtd%d: [%s] removed", dev->mtd_info.index, ++ dev->mtd_info.name + strlen("blkmtd: ")); ++ list_del(&dev->list); + free_device(dev); + } + } +@@ -1020,6 +804,7 @@ + { + int i; + ++ info("version " VERSION); + /* Check args - device[0] is the bare minimum*/ + if(!device[0]) { + err("error: missing `device' name\n"); +@@ -1030,28 +815,9 @@ + add_device(device[i], ro[i], erasesz[i] << 10); + + if(list_empty(&blkmtd_device_list)) +- goto init_err; +- +- info("version " VERSION); +- +-#ifdef BLKMTD_PROC_DEBUG +- /* create proc entry */ +- DEBUG(2, "Creating /proc/blkmtd_debug\n"); +- blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, +- NULL, blkmtd_proc_read, NULL); +- if(blkmtd_proc == NULL) { +- err("Cant create /proc/blkmtd_debug"); +- } else { +- blkmtd_proc->owner = THIS_MODULE; +- } +-#endif ++ return -EINVAL; + +- if(!list_empty(&blkmtd_device_list)) +- /* Everything is ok if we got here */ + return 0; +- +- init_err: +- return -EINVAL; + } + + module_init(init_blkmtd); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/devices/block2mtd.c +@@ -0,0 +1,494 @@ ++/* ++ * $Id$ ++ * ++ * block2mtd.c - create an mtd from a block device ++ * ++ * Copyright (C) 2001,2002 Simon Evans ++ * Copyright (C) 2004,2005 Jörn Engel ++ * ++ * Licence: GPL ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define VERSION "$Revision$" ++ ++ ++#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) ++#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) ++ ++ ++/* Info for the block device */ ++struct block2mtd_dev { ++ struct list_head list; ++ struct block_device *blkdev; ++ struct mtd_info mtd; ++ struct semaphore write_mutex; ++}; ++ ++ ++/* Static info about the MTD, used in cleanup_module */ ++static LIST_HEAD(blkmtd_device_list); ++ ++ ++#define PAGE_READAHEAD 64 ++void cache_readahead(struct address_space *mapping, int index) ++{ ++ filler_t *filler = (filler_t*)mapping->a_ops->readpage; ++ int i, pagei; ++ unsigned ret = 0; ++ unsigned long end_index; ++ struct page *page; ++ LIST_HEAD(page_pool); ++ struct inode *inode = mapping->host; ++ loff_t isize = i_size_read(inode); ++ ++ if (!isize) { ++ printk(KERN_INFO "iSize=0 in cache_readahead\n"); ++ return; ++ } ++ ++ end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); ++ ++ spin_lock_irq(&mapping->tree_lock); ++ for (i = 0; i < PAGE_READAHEAD; i++) { ++ pagei = index + i; ++ if (pagei > end_index) { ++ printk(KERN_INFO "Overrun end of disk in cache readahead\n"); ++ break; ++ } ++ page = radix_tree_lookup(&mapping->page_tree, pagei); ++ if (page && (!i)) ++ break; ++ if (page) ++ continue; ++ spin_unlock_irq(&mapping->tree_lock); ++ page = page_cache_alloc_cold(mapping); ++ spin_lock_irq(&mapping->tree_lock); ++ if (!page) ++ break; ++ page->index = pagei; ++ list_add(&page->lru, &page_pool); ++ ret++; ++ } ++ spin_unlock_irq(&mapping->tree_lock); ++ if (ret) ++ read_cache_pages(mapping, &page_pool, filler, NULL); ++} ++ ++ ++static struct page* page_readahead(struct address_space *mapping, int index) ++{ ++ filler_t *filler = (filler_t*)mapping->a_ops->readpage; ++ cache_readahead(mapping, index); ++ return read_cache_page(mapping, index, filler, NULL); ++} ++ ++ ++/* erase a specified part of the device */ ++static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) ++{ ++ struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; ++ struct page *page; ++ int index = to >> PAGE_SHIFT; // page index ++ int pages = len >> PAGE_SHIFT; ++ u_long *p; ++ u_long *max; ++ ++ while (pages) { ++ page = page_readahead(mapping, index); ++ if (!page) ++ return -ENOMEM; ++ if (IS_ERR(page)) ++ return PTR_ERR(page); ++ ++ max = (u_long*)page_address(page) + PAGE_SIZE; ++ for (p=(u_long*)page_address(page); ppriv; ++ size_t from = instr->addr; ++ size_t len = instr->len; ++ int err; ++ ++ instr->state = MTD_ERASING; ++ down(&dev->write_mutex); ++ err = _block2mtd_erase(dev, from, len); ++ up(&dev->write_mutex); ++ if (err) { ++ ERROR("erase failed err = %d", err); ++ instr->state = MTD_ERASE_FAILED; ++ } else ++ instr->state = MTD_ERASE_DONE; ++ ++ instr->state = MTD_ERASE_DONE; ++ mtd_erase_callback(instr); ++ return err; ++} ++ ++ ++static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct block2mtd_dev *dev = mtd->priv; ++ struct page *page; ++ int index = from >> PAGE_SHIFT; ++ int offset = from & (PAGE_SIZE-1); ++ int cpylen; ++ ++ if (from > mtd->size) ++ return -EINVAL; ++ if (from + len > mtd->size) ++ len = mtd->size - from; ++ ++ if (retlen) ++ *retlen = 0; ++ ++ while (len) { ++ if ((offset + len) > PAGE_SIZE) ++ cpylen = PAGE_SIZE - offset; // multiple pages ++ else ++ cpylen = len; // this page ++ len = len - cpylen; ++ ++ // Get page ++ page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); ++ if (!page) ++ return -ENOMEM; ++ if (IS_ERR(page)) ++ return PTR_ERR(page); ++ ++ memcpy(buf, page_address(page) + offset, cpylen); ++ page_cache_release(page); ++ ++ if (retlen) ++ *retlen += cpylen; ++ buf += cpylen; ++ offset = 0; ++ index++; ++ } ++ return 0; ++} ++ ++ ++/* write data to the underlying device */ ++static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, ++ loff_t to, size_t len, size_t *retlen) ++{ ++ struct page *page; ++ struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; ++ int index = to >> PAGE_SHIFT; // page index ++ int offset = to & ~PAGE_MASK; // page offset ++ int cpylen; ++ ++ if (retlen) ++ *retlen = 0; ++ while (len) { ++ if ((offset+len) > PAGE_SIZE) ++ cpylen = PAGE_SIZE - offset; // multiple pages ++ else ++ cpylen = len; // this page ++ len = len - cpylen; ++ ++ // Get page ++ page = page_readahead(mapping, index); ++ if (!page) ++ return -ENOMEM; ++ if (IS_ERR(page)) ++ return PTR_ERR(page); ++ ++ if (memcmp(page_address(page)+offset, buf, cpylen)) { ++ lock_page(page); ++ memcpy(page_address(page) + offset, buf, cpylen); ++ set_page_dirty(page); ++ unlock_page(page); ++ } ++ page_cache_release(page); ++ ++ if (retlen) ++ *retlen += cpylen; ++ ++ buf += cpylen; ++ offset = 0; ++ index++; ++ } ++ return 0; ++} ++static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct block2mtd_dev *dev = mtd->priv; ++ int err; ++ ++ if (!len) ++ return 0; ++ if (to >= mtd->size) ++ return -ENOSPC; ++ if (to + len > mtd->size) ++ len = mtd->size - to; ++ ++ down(&dev->write_mutex); ++ err = _block2mtd_write(dev, buf, to, len, retlen); ++ up(&dev->write_mutex); ++ if (err > 0) ++ err = 0; ++ return err; ++} ++ ++ ++/* sync the device - wait until the write queue is empty */ ++static void block2mtd_sync(struct mtd_info *mtd) ++{ ++ struct block2mtd_dev *dev = mtd->priv; ++ sync_blockdev(dev->blkdev); ++ return; ++} ++ ++ ++static void block2mtd_free_device(struct block2mtd_dev *dev) ++{ ++ if (!dev) ++ return; ++ ++ kfree(dev->mtd.name); ++ ++ if (dev->blkdev) { ++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); ++ close_bdev_excl(dev->blkdev); ++ } ++ ++ kfree(dev); ++} ++ ++ ++/* FIXME: ensure that mtd->size % erase_size == 0 */ ++static struct block2mtd_dev *add_device(char *devname, int erase_size) ++{ ++ struct block_device *bdev; ++ struct block2mtd_dev *dev; ++ ++ if (!devname) ++ return NULL; ++ ++ dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); ++ if (!dev) ++ return NULL; ++ memset(dev, 0, sizeof(*dev)); ++ ++ /* Get a handle on the device */ ++ bdev = open_bdev_excl(devname, O_RDWR, NULL); ++ if (IS_ERR(bdev)) { ++ ERROR("error: cannot open device %s", devname); ++ goto devinit_err; ++ } ++ dev->blkdev = bdev; ++ ++ if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { ++ ERROR("attempting to use an MTD device as a block device"); ++ goto devinit_err; ++ } ++ ++ atomic_set(&bdev->bd_inode->i_mapping->truncate_count, 0); ++ init_MUTEX(&dev->write_mutex); ++ ++ /* Setup the MTD structure */ ++ /* make the name contain the block device in */ ++ dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), ++ GFP_KERNEL); ++ if (!dev->mtd.name) ++ goto devinit_err; ++ ++ sprintf(dev->mtd.name, "block2mtd: %s", devname); ++ ++ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; ++ dev->mtd.erasesize = erase_size; ++ dev->mtd.type = MTD_RAM; ++ dev->mtd.flags = MTD_CAP_RAM; ++ dev->mtd.erase = block2mtd_erase; ++ dev->mtd.write = block2mtd_write; ++ dev->mtd.writev = default_mtd_writev; ++ dev->mtd.sync = block2mtd_sync; ++ dev->mtd.read = block2mtd_read; ++ dev->mtd.readv = default_mtd_readv; ++ dev->mtd.priv = dev; ++ dev->mtd.owner = THIS_MODULE; ++ ++ if (add_mtd_device(&dev->mtd)) { ++ /* Device didnt get added, so free the entry */ ++ goto devinit_err; ++ } ++ list_add(&dev->list, &blkmtd_device_list); ++ INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, ++ dev->mtd.name + strlen("blkmtd: "), ++ dev->mtd.erasesize >> 10, dev->mtd.erasesize); ++ return dev; ++ ++devinit_err: ++ block2mtd_free_device(dev); ++ return NULL; ++} ++ ++ ++static int ustrtoul(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long result = simple_strtoul(cp, endp, base); ++ switch (**endp) { ++ case 'G' : ++ result *= 1024; ++ case 'M': ++ result *= 1024; ++ case 'k': ++ result *= 1024; ++ /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ ++ if ((*endp)[1] == 'i') ++ (*endp) += 2; ++ } ++ return result; ++} ++ ++ ++static int parse_num32(u32 *num32, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ ++ *num32 = n; ++ return 0; ++} ++ ++ ++static int parse_name(char **pname, const char *token, size_t limit) ++{ ++ size_t len; ++ char *name; ++ ++ len = strlen(token) + 1; ++ if (len > limit) ++ return -ENOSPC; ++ ++ name = kmalloc(len, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ strcpy(name, token); ++ ++ *pname = name; ++ return 0; ++} ++ ++ ++static inline void kill_final_newline(char *str) ++{ ++ char *newline = strrchr(str, '\n'); ++ if (newline && !newline[1]) ++ *newline = 0; ++} ++ ++ ++#define parse_err(fmt, args...) do { \ ++ ERROR("block2mtd: " fmt "\n", ## args); \ ++ return 0; \ ++} while (0) ++ ++static int block2mtd_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ ++ char *token[2]; ++ char *name; ++ size_t erase_size = PAGE_SIZE; ++ int i, ret; ++ ++ if (strnlen(val, sizeof(buf)) >= sizeof(buf)) ++ parse_err("parameter too long"); ++ ++ strcpy(str, val); ++ kill_final_newline(str); ++ ++ for (i=0; i<2; i++) ++ token[i] = strsep(&str, ","); ++ ++ if (str) ++ parse_err("too many arguments"); ++ ++ if (!token[0]) ++ parse_err("no argument"); ++ ++ ret = parse_name(&name, token[0], 80); ++ if (ret == -ENOMEM) ++ parse_err("out of memory"); ++ if (ret == -ENOSPC) ++ parse_err("name too long"); ++ if (ret) ++ return 0; ++ ++ if (token[1]) { ++ ret = parse_num32(&erase_size, token[1]); ++ if (ret) ++ parse_err("illegal erase size"); ++ } ++ ++ add_device(name, erase_size); ++ ++ return 0; ++} ++ ++ ++module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); ++MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); ++ ++static int __init block2mtd_init(void) ++{ ++ INFO("version " VERSION); ++ return 0; ++} ++ ++ ++static void __devexit block2mtd_exit(void) ++{ ++ struct list_head *pos, *next; ++ ++ /* Remove the MTD devices */ ++ list_for_each_safe(pos, next, &blkmtd_device_list) { ++ struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); ++ block2mtd_sync(&dev->mtd); ++ del_mtd_device(&dev->mtd); ++ INFO("mtd%d: [%s] removed", dev->mtd.index, ++ dev->mtd.name + strlen("blkmtd: ")); ++ list_del(&dev->list); ++ block2mtd_free_device(dev); ++ } ++} ++ ++ ++module_init(block2mtd_init); ++module_exit(block2mtd_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Simon Evans and others"); ++MODULE_DESCRIPTION("Emulate an MTD using a block device"); +--- linux-2.4.21/drivers/mtd/devices/doc2000.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/doc2000.c +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -19,12 +19,14 @@ + #include + #include + #include ++#include + + #include + #include + #include + + #define DOC_SUPPORT_2000 ++#define DOC_SUPPORT_2000TSOP + #define DOC_SUPPORT_MILLENNIUM + + #ifdef DOC_SUPPORT_2000 +@@ -33,7 +35,7 @@ + #define DoC_is_2000(doc) (0) + #endif + +-#ifdef DOC_SUPPORT_MILLENNIUM ++#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) + #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) + #else + #define DoC_is_Millennium(doc) (0) +@@ -53,9 +55,12 @@ + static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); + static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++ size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); ++static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel); + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); + static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, +@@ -84,7 +89,7 @@ + /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ + static int _DoC_WaitReady(struct DiskOnChip *doc) + { +- unsigned long docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; + unsigned long timeo = jiffies + (HZ * 10); + + DEBUG(MTD_DEBUG_LEVEL3, +@@ -92,6 +97,10 @@ + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ /* issue 2 read from NOP register after reading from CDSNControl register ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(doc, 2); ++ + if (time_after(jiffies, timeo)) { + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + return -EIO; +@@ -105,7 +114,8 @@ + + static inline int DoC_WaitReady(struct DiskOnChip *doc) + { +- unsigned long docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; ++ + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; + +@@ -131,7 +141,7 @@ + static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, + unsigned char xtraflags) + { +- unsigned long docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags |= CDSN_CTRL_FLASH_IO; +@@ -145,6 +155,8 @@ + + /* Send the command */ + WriteDOC_(command, docptr, doc->ioreg); ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(command, docptr, WritePipeTerm); + + /* Lower the CLE line */ + WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); +@@ -161,10 +173,8 @@ + static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) + { +- unsigned long docptr; + int i; +- +- docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags1 |= CDSN_CTRL_FLASH_IO; +@@ -206,6 +216,9 @@ + } + } + ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(ofs & 0xff, docptr, WritePipeTerm); ++ + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ + + /* FIXME: The SlowIO's for millennium could be replaced by +@@ -226,11 +239,9 @@ + { + volatile int dummy; + int modulus = 0xffff; +- unsigned long docptr; ++ void __iomem *docptr = doc->virtadr; + int i; + +- docptr = doc->virtadr; +- + if (len <= 0) + return; + +@@ -257,11 +268,9 @@ + /* Write a buffer to DoC, taking care of Millennium odditys */ + static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len) + { +- unsigned long docptr; ++ void __iomem *docptr = doc->virtadr; + int i; + +- docptr = doc->virtadr; +- + if (len <= 0) + return; + +@@ -278,7 +287,7 @@ + + static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) + { +- unsigned long docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; + + /* Software requirement 11.4.4 before writing DeviceSelect */ + /* Deassert the CE line to eliminate glitches on the FCE# outputs */ +@@ -302,7 +311,7 @@ + + static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) + { +- unsigned long docptr = doc->virtadr; ++ void __iomem *docptr = doc->virtadr; + + /* Select the floor (bank) of chips required */ + WriteDOC(floor, docptr, FloorSelect); +@@ -344,15 +353,25 @@ + + /* Read the manufacturer and device id codes from the device */ + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ if (DoC_is_Millennium(doc)) { ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ mfr = ReadDOC(doc->virtadr, LastDataRead); ++ ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ id = ReadDOC(doc->virtadr, LastDataRead); ++ } else { ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); ++ } + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) +@@ -387,10 +406,9 @@ + doc->mfr = mfr; + doc->id = id; + doc->chipshift = +- nand_flash_ids[i].chipshift; +- doc->page256 = nand_flash_ids[i].page256; +- doc->pageadrlen = +- nand_flash_ids[i].chipshift > 25 ? 3 : 2; ++ ffs((nand_flash_ids[i].chipsize << 20)) - 1; ++ doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0; ++ doc->pageadrlen = doc->chipshift > 25 ? 3 : 2; + doc->erasesize = + nand_flash_ids[i].erasesize; + return 1; +@@ -410,20 +428,16 @@ + + /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +-static void DoC_ScanChips(struct DiskOnChip *this) ++static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) + { + int floor, chip; + int numchips[MAX_FLOORS]; +- int maxchips = MAX_CHIPS; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + +- if (DoC_is_Millennium(this)) +- maxchips = MAX_CHIPS_MIL; +- + /* For each floor, find the number of valid chips it contains */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + ret = 1; +@@ -513,39 +527,54 @@ + */ + static void DoC2k_init(struct mtd_info *mtd) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + struct DiskOnChip *old = NULL; ++ int maxchips; + + /* We must avoid being called twice for the same device. */ + + if (doc2klist) +- old = (struct DiskOnChip *) doc2klist->priv; ++ old = doc2klist->priv; + + while (old) { + if (DoC2k_is_alias(old, this)) { + printk(KERN_NOTICE + "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", + this->physadr); +- iounmap((void *) this->virtadr); ++ iounmap(this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) +- old = (struct DiskOnChip *) old->nextdoc->priv; ++ old = old->nextdoc->priv; + else + old = NULL; + } + + + switch (this->ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ mtd->name = "DiskOnChip 2000 TSOP"; ++ this->ioreg = DoC_Mil_CDSN_IO; ++ /* Pretend it's a Millennium */ ++ this->ChipID = DOC_ChipID_DocMil; ++ maxchips = MAX_CHIPS; ++ break; + case DOC_ChipID_Doc2k: + mtd->name = "DiskOnChip 2000"; + this->ioreg = DoC_2k_CDSN_IO; ++ maxchips = MAX_CHIPS; + break; + case DOC_ChipID_DocMil: + mtd->name = "DiskOnChip Millennium"; + this->ioreg = DoC_Mil_CDSN_IO; ++ maxchips = MAX_CHIPS_MIL; + break; ++ default: ++ printk("Unknown ChipID 0x%02x\n", this->ChipID); ++ kfree(mtd); ++ iounmap(this->virtadr); ++ return; + } + + printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, +@@ -553,11 +582,12 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + mtd->erasesize = 0; + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +@@ -565,6 +595,7 @@ + mtd->write = doc_write; + mtd->read_ecc = doc_read_ecc; + mtd->write_ecc = doc_write_ecc; ++ mtd->writev_ecc = doc_writev_ecc; + mtd->read_oob = doc_read_oob; + mtd->write_oob = doc_write_oob; + mtd->sync = NULL; +@@ -577,11 +608,11 @@ + init_MUTEX(&this->lock); + + /* Ident all the chips present. */ +- DoC_ScanChips(this); ++ DoC_ScanChips(this, maxchips); + + if (!this->totlen) { + kfree(mtd); +- iounmap((void *) this->virtadr); ++ iounmap(this->virtadr); + } else { + this->nextdoc = doc2klist; + doc2klist = mtd; +@@ -596,20 +627,19 @@ + size_t * retlen, u_char * buf) + { + /* Just a special case of doc_read_ecc */ +- return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); ++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); + } + + static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel) ++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; +- unsigned long docptr; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip; + unsigned char syndrome[6]; + volatile char dummy; + int i, len256 = 0, ret=0; +- +- docptr = this->virtadr; ++ size_t left = len; + + /* Don't allow read past end of device */ + if (from >= this->totlen) +@@ -617,6 +647,10 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; +@@ -673,7 +707,7 @@ + DoC_ReadBuf(this, &buf[len256], len - len256); + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ +@@ -730,11 +764,16 @@ + + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ +- if(0 == ((from + *retlen) & 0x1ff)) ++ if(0 == ((from + len) & 0x1ff)) + { + DoC_WaitReady(this); + } + ++ from += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + + return ret; +@@ -744,21 +783,21 @@ + size_t * retlen, const u_char * buf) + { + char eccbuf[6]; +- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); ++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); + } + + static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, +- u_char * eccbuf, int oobsel) ++ u_char * eccbuf, struct nand_oobinfo *oobsel) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ +- unsigned long docptr; ++ void __iomem *docptr = this->virtadr; + volatile char dummy; + int len256 = 0; + struct Nand *mychip; +- +- docptr = this->virtadr; ++ size_t left = len; ++ int status; + + /* Don't allow write past end of device */ + if (to >= this->totlen) +@@ -766,15 +805,21 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ((to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + + /* The ECC will not be calculated correctly if less than 512 is written */ ++/* DBB- + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); ++ -DBB */ + + /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ + +@@ -853,6 +898,9 @@ + WriteDOC_(0, docptr, this->ioreg); + } + ++ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, ++ CDSNControl); ++ + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di = 0; di < 6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); +@@ -874,10 +922,16 @@ + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming flash\n"); + /* Error in programming */ + *retlen = 0; +@@ -886,7 +940,7 @@ + } + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + unsigned char x[8]; +@@ -901,25 +955,90 @@ + x[7]=0x55; + + ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); ++ if (ret) { + up(&this->lock); + return ret; + } ++ } ++ ++ to += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + return 0; + } + ++static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel) ++{ ++ static char static_buf[512]; ++ static DECLARE_MUTEX(writev_buf_sem); ++ ++ size_t totretlen = 0; ++ size_t thisvecofs = 0; ++ int ret= 0; ++ ++ down(&writev_buf_sem); ++ ++ while(count) { ++ size_t thislen, thisretlen; ++ unsigned char *buf; ++ ++ buf = vecs->iov_base + thisvecofs; ++ thislen = vecs->iov_len - thisvecofs; ++ ++ ++ if (thislen >= 512) { ++ thislen = thislen & ~(512-1); ++ thisvecofs += thislen; ++ } else { ++ /* Not enough to fill a page. Copy into buf */ ++ memcpy(static_buf, buf, thislen); ++ buf = &static_buf[thislen]; ++ ++ while(count && thislen < 512) { ++ vecs++; ++ count--; ++ thisvecofs = min((512-thislen), vecs->iov_len); ++ memcpy(buf, vecs->iov_base, thisvecofs); ++ thislen += thisvecofs; ++ buf += thisvecofs; ++ } ++ buf = static_buf; ++ } ++ if (count && thisvecofs == vecs->iov_len) { ++ thisvecofs = 0; ++ vecs++; ++ count--; ++ } ++ ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); ++ ++ totretlen += thisretlen; ++ ++ if (ret || thisretlen != thislen) ++ break; ++ ++ to += thislen; ++ } ++ ++ up(&writev_buf_sem); ++ *retlen = totretlen; ++ return ret; ++} ++ ++ + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, u_char * buf) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + int len256 = 0, ret; +- unsigned long docptr; + struct Nand *mychip; + + down(&this->lock); + +- docptr = this->virtadr; +- + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { +@@ -972,11 +1091,12 @@ + static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, const u_char * buf) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + int len256 = 0; +- unsigned long docptr = this->virtadr; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + volatile int dummy; ++ int status; + + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); +@@ -1025,10 +1145,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1044,10 +1170,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1062,7 +1194,7 @@ + static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, const u_char * buf) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + int ret; + + down(&this->lock); +@@ -1074,12 +1206,13 @@ + + static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) + { +- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + __u32 ofs = instr->addr; + __u32 len = instr->len; + volatile int dummy; +- unsigned long docptr; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip; ++ int status; + + down(&this->lock); + +@@ -1090,8 +1223,6 @@ + + instr->state = MTD_ERASING; + +- docptr = this->virtadr; +- + /* FIXME: Do this in the background. Use timers or schedule_task() */ + while(len) { + mychip = &this->chips[ofs >> this->chipshift]; +@@ -1111,10 +1242,16 @@ + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error erasing at 0x%x\n", ofs); + /* There was an error */ + instr->state = MTD_ERASE_FAILED; +@@ -1126,8 +1263,7 @@ + instr->state = MTD_ERASE_DONE; + + callback: +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + up(&this->lock); + return 0; +@@ -1140,7 +1276,7 @@ + * + ****************************************************************************/ + +-int __init init_doc2000(void) ++static int __init init_doc2000(void) + { + inter_module_register(im_name, THIS_MODULE, &DoC2k_init); + return 0; +@@ -1152,12 +1288,12 @@ + struct DiskOnChip *this; + + while ((mtd = doc2klist)) { +- this = (struct DiskOnChip *) mtd->priv; ++ this = mtd->priv; + doc2klist = this->nextdoc; + + del_mtd_device(mtd); + +- iounmap((void *) this->virtadr); ++ iounmap(this->virtadr); + kfree(this->chips); + kfree(mtd); + } +--- linux-2.4.21/drivers/mtd/devices/doc2001.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/doc2001.c +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -37,9 +38,11 @@ + static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); + static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); + static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); + static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, +@@ -49,7 +52,7 @@ + static struct mtd_info *docmillist = NULL; + + /* Perform the required delay cycles by reading from the NOP register */ +-static void DoC_Delay(unsigned long docptr, unsigned short cycles) ++static void DoC_Delay(void __iomem * docptr, unsigned short cycles) + { + volatile char dummy; + int i; +@@ -59,7 +62,7 @@ + } + + /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +-static int _DoC_WaitReady(unsigned long docptr) ++static int _DoC_WaitReady(void __iomem * docptr) + { + unsigned short c = 0xffff; + +@@ -76,7 +79,7 @@ + return (c == 0); + } + +-static inline int DoC_WaitReady(unsigned long docptr) ++static inline int DoC_WaitReady(void __iomem * docptr) + { + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; +@@ -100,7 +103,7 @@ + with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +-static inline void DoC_Command(unsigned long docptr, unsigned char command, ++static inline void DoC_Command(void __iomem * docptr, unsigned char command, + unsigned char xtraflags) + { + /* Assert the CLE (Command Latch Enable) line to the flash chip */ +@@ -120,7 +123,7 @@ + with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +-static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs, ++static inline void DoC_Address(void __iomem * docptr, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) + { + /* Assert the ALE (Address Latch Enable) line to the flash chip */ +@@ -158,7 +161,7 @@ + } + + /* DoC_SelectChip: Select a given flash chip within the current floor */ +-static int DoC_SelectChip(unsigned long docptr, int chip) ++static int DoC_SelectChip(void __iomem * docptr, int chip) + { + /* Select the individual flash chip requested */ + WriteDOC(chip, docptr, CDSNDeviceSelect); +@@ -169,7 +172,7 @@ + } + + /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ +-static int DoC_SelectFloor(unsigned long docptr, int floor) ++static int DoC_SelectFloor(void __iomem * docptr, int floor) + { + /* Select the floor (bank) of chips required */ + WriteDOC(floor, docptr, FloorSelect); +@@ -226,7 +229,7 @@ + mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name); + doc->mfr = mfr; + doc->id = id; +- doc->chipshift = nand_flash_ids[i].chipshift; ++ doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; + break; + } + } +@@ -332,23 +335,23 @@ + */ + static void DoCMil_init(struct mtd_info *mtd) + { +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + struct DiskOnChip *old = NULL; + + /* We must avoid being called twice for the same device. */ + if (docmillist) +- old = (struct DiskOnChip *)docmillist->priv; ++ old = docmillist->priv; + + while (old) { + if (DoCMil_is_alias(this, old)) { + printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at " + "0x%lX - already configured\n", this->physadr); +- iounmap((void *)this->virtadr); ++ iounmap(this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) +- old = (struct DiskOnChip *)old->nextdoc->priv; ++ old = old->nextdoc->priv; + else + old = NULL; + } +@@ -359,14 +362,15 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + +- /* FIXME: erase size is not always 8kB */ ++ /* FIXME: erase size is not always 8KiB */ + mtd->erasesize = 0x2000; + + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +@@ -388,7 +392,7 @@ + + if (!this->totlen) { + kfree(mtd); +- iounmap((void *)this->virtadr); ++ iounmap(this->virtadr); + } else { + this->nextdoc = docmillist; + docmillist = mtd; +@@ -402,17 +406,18 @@ + size_t *retlen, u_char *buf) + { + /* Just a special case of doc_read_ecc */ +- return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); ++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); + } + + static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) + { + int i, ret; + volatile char dummy; + unsigned char syndrome[6]; +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; +- unsigned long docptr = this->virtadr; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[from >> (this->chipshift)]; + + /* Don't allow read past end of device */ +@@ -528,16 +533,17 @@ + size_t *retlen, const u_char *buf) + { + char eccbuf[6]; +- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); ++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); + } + + static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel) ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) + { + int i,ret = 0; + volatile char dummy; +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; +- unsigned long docptr = this->virtadr; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[to >> (this->chipshift)]; + + /* Don't allow write past end of device */ +@@ -671,8 +677,8 @@ + int i; + #endif + volatile char dummy; +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; +- unsigned long docptr = this->virtadr; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + + /* Find the chip which is to be used and select it */ +@@ -723,8 +729,8 @@ + #endif + volatile char dummy; + int ret = 0; +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; +- unsigned long docptr = this->virtadr; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + + /* Find the chip which is to be used and select it */ +@@ -790,10 +796,10 @@ + int doc_erase (struct mtd_info *mtd, struct erase_info *instr) + { + volatile char dummy; +- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ struct DiskOnChip *this = mtd->priv; + __u32 ofs = instr->addr; + __u32 len = instr->len; +- unsigned long docptr = this->virtadr; ++ void __iomem *docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + + if (len != mtd->erasesize) +@@ -839,8 +845,7 @@ + instr->state = MTD_ERASE_DONE; + dummy = ReadDOC(docptr, LastDataRead); + +- if (instr->callback) +- instr->callback(instr); ++ mtd_erase_callback(instr); + + return 0; + } +@@ -851,7 +856,7 @@ + * + ****************************************************************************/ + +-int __init init_doc2001(void) ++static int __init init_doc2001(void) + { + inter_module_register(im_name, THIS_MODULE, &DoCMil_init); + return 0; +@@ -863,12 +868,12 @@ + struct DiskOnChip *this; + + while ((mtd=docmillist)) { +- this = (struct DiskOnChip *)mtd->priv; ++ this = mtd->priv; + docmillist = this->nextdoc; + + del_mtd_device(mtd); + +- iounmap((void *)this->virtadr); ++ iounmap(this->virtadr); + kfree(this->chips); + kfree(mtd); + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/devices/doc2001plus.c +@@ -0,0 +1,1154 @@ ++/* ++ * Linux driver for Disk-On-Chip Millennium Plus ++ * ++ * (c) 2002-2003 Greg Ungerer ++ * (c) 2002-2003 SnapGear Inc ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * (c) 1999, 2000 David Woodhouse ++ * ++ * $Id$ ++ * ++ * Released under GPL ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* #define ECC_DEBUG */ ++ ++/* I have no idea why some DoC chips can not use memcop_form|to_io(). ++ * This may be due to the different revisions of the ASIC controller built-in or ++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment ++ * this:*/ ++#undef USE_MEMCPY ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); ++ ++static struct mtd_info *docmilpluslist = NULL; ++ ++ ++/* Perform the required delay cycles by writing to the NOP register */ ++static void DoC_Delay(void __iomem * docptr, int cycles) ++{ ++ int i; ++ ++ for (i = 0; (i < cycles); i++) ++ WriteDOC(0, docptr, Mplus_NOP); ++} ++ ++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) ++ ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(void __iomem * docptr) ++{ ++ unsigned int c = 0xffff; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "_DoC_WaitReady called for out-of-line wait\n"); ++ ++ /* Out-of-line routine to wait for chip response */ ++ while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) ++ ; ++ ++ if (c == 0) ++ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); ++ ++ return (c == 0); ++} ++ ++static inline int DoC_WaitReady(void __iomem * docptr) ++{ ++ /* This is inline, to optimise the common case, where it's ready instantly */ ++ int ret = 0; ++ ++ /* read form NOP register should be issued prior to the read from CDSNControl ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(docptr, 4); ++ ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(docptr); ++ ++ return ret; ++} ++ ++/* For some reason the Millennium Plus seems to occassionally put itself ++ * into reset mode. For me this happens randomly, with no pattern that I ++ * can detect. M-systems suggest always check this on any block level ++ * operation and setting to normal mode if in reset mode. ++ */ ++static inline void DoC_CheckASIC(void __iomem * docptr) ++{ ++ /* Make sure the DoC is in normal mode */ ++ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { ++ WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); ++ WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); ++ } ++} ++ ++/* DoC_Command: Send a flash command to the flash chip through the Flash ++ * command register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Command(void __iomem * docptr, unsigned char command, ++ unsigned char xtraflags) ++{ ++ WriteDOC(command, docptr, Mplus_FlashCmd); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_Address: Set the current address for the flash chip through the Flash ++ * Address register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, ++ unsigned long ofs, unsigned char xtraflags1, ++ unsigned char xtraflags2) ++{ ++ void __iomem * docptr = doc->virtadr; ++ ++ /* Allow for possible Mill Plus internal flash interleaving */ ++ ofs >>= doc->interleave; ++ ++ switch (numbytes) { ++ case 1: ++ /* Send single byte, bits 0-7. */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 2: ++ /* Send bits 9-16 followed by 17-23 */ ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 3: ++ /* Send 0-7, 9-16, then 17-23 */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ default: ++ return; ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_SelectChip: Select a given flash chip within the current floor */ ++static int DoC_SelectChip(void __iomem * docptr, int chip) ++{ ++ /* No choice for flash chip on Millennium Plus */ ++ return 0; ++} ++ ++/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ ++static int DoC_SelectFloor(void __iomem * docptr, int floor) ++{ ++ WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); ++ return 0; ++} ++ ++/* ++ * Translate the given offset into the appropriate command and offset. ++ * This does the mapping using the 16bit interleave layout defined by ++ * M-Systems, and looks like this for a sector pair: ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ */ ++/* FIXME: This lives in INFTL not here. Other users of flash devices ++ may not want it */ ++static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ struct DiskOnChip *this = mtd->priv; ++ ++ if (this->interleave) { ++ unsigned int ofs = *from & 0x3ff; ++ unsigned int cmd; ++ ++ if (ofs < 512) { ++ cmd = NAND_CMD_READ0; ++ ofs &= 0x1ff; ++ } else if (ofs < 1014) { ++ cmd = NAND_CMD_READ1; ++ ofs = (ofs & 0x1ff) + 10; ++ } else { ++ cmd = NAND_CMD_READOOB; ++ ofs = ofs - 1014; ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++ } else { ++ /* No interleave */ ++ if ((*from) & 0x100) ++ return NAND_CMD_READ1; ++ return NAND_CMD_READ0; ++ } ++} ++ ++static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ if (*from & 0x200) { ++ cmd = NAND_CMD_READOOB; ++ ofs = 10 + (*from & 0xf); ++ } else { ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0xf); ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0x200) ? 8 : 6; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READOOB; ++ ofs = (*from & 0x200) ? 24 : 16; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); ++#endif ++} ++ ++static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); ++#endif ++} ++ ++/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ ++static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ++{ ++ int mfr, id, i, j; ++ volatile char dummy; ++ void __iomem * docptr = doc->virtadr; ++ ++ /* Page in the required floor/chip */ ++ DoC_SelectFloor(docptr, floor); ++ DoC_SelectChip(docptr, chip); ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Read the NAND chip ID: 1. Send ReadID command */ ++ DoC_Command(docptr, NAND_CMD_READID, 0); ++ ++ /* Read the NAND chip ID: 2. Send address byte zero */ ++ DoC_Address(doc, 1, 0x00, 0, 0x00); ++ ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* Read the manufacturer and device id codes of the flash device through ++ CDSN IO register see Software Requirement 11.4 item 5.*/ ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ mfr = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ id = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* No response - return failure */ ++ if (mfr == 0xff || mfr == 0) ++ return 0; ++ ++ for (i = 0; nand_flash_ids[i].name != NULL; i++) { ++ if (id == nand_flash_ids[i].id) { ++ /* Try to identify manufacturer */ ++ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { ++ if (nand_manuf_ids[j].id == mfr) ++ break; ++ } ++ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " ++ "Chip ID: %2.2X (%s:%s)\n", mfr, id, ++ nand_manuf_ids[j].name, nand_flash_ids[i].name); ++ doc->mfr = mfr; ++ doc->id = id; ++ doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; ++ doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; ++ break; ++ } ++ } ++ ++ if (nand_flash_ids[i].name == NULL) ++ return 0; ++ return 1; ++} ++ ++/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ ++static void DoC_ScanChips(struct DiskOnChip *this) ++{ ++ int floor, chip; ++ int numchips[MAX_FLOORS_MPLUS]; ++ int ret; ++ ++ this->numchips = 0; ++ this->mfr = 0; ++ this->id = 0; ++ ++ /* Work out the intended interleave setting */ ++ this->interleave = 0; ++ if (this->ChipID == DOC_ChipID_DocMilPlus32) ++ this->interleave = 1; ++ ++ /* Check the ASIC agrees */ ++ if ( (this->interleave << 2) != ++ (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { ++ u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); ++ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", ++ this->interleave?"on (16-bit)":"off (8-bit)"); ++ conf ^= 4; ++ WriteDOC(conf, this->virtadr, Mplus_Configuration); ++ } ++ ++ /* For each floor, find the number of valid chips it contains */ ++ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { ++ numchips[floor] = 0; ++ for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { ++ ret = DoC_IdentChip(this, floor, chip); ++ if (ret) { ++ numchips[floor]++; ++ this->numchips++; ++ } ++ } ++ } ++ /* If there are none at all that we recognise, bail */ ++ if (!this->numchips) { ++ printk("No flash chips recognised.\n"); ++ return; ++ } ++ ++ /* Allocate an array to hold the information for each chip */ ++ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); ++ if (!this->chips){ ++ printk("MTD: No memory for allocating chip info structures\n"); ++ return; ++ } ++ ++ /* Fill out the chip array with {floor, chipno} for each ++ * detected chip in the device. */ ++ for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { ++ for (chip = 0 ; chip < numchips[floor] ; chip++) { ++ this->chips[ret].floor = floor; ++ this->chips[ret].chip = chip; ++ this->chips[ret].curadr = 0; ++ this->chips[ret].curmode = 0x50; ++ ret++; ++ } ++ } ++ ++ /* Calculate and print the total size of the device */ ++ this->totlen = this->numchips * (1 << this->chipshift); ++ printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", ++ this->numchips ,this->totlen >> 20); ++} ++ ++static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) ++{ ++ int tmp1, tmp2, retval; ++ ++ if (doc1->physadr == doc2->physadr) ++ return 1; ++ ++ /* Use the alias resolution register which was set aside for this ++ * purpose. If it's value is the same on both chips, they might ++ * be the same chip, and we write to one and check for a change in ++ * the other. It's unclear if this register is usuable in the ++ * DoC 2000 (it's in the Millennium docs), but it seems to work. */ ++ tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp1 != tmp2) ++ return 0; ++ ++ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp2 == (tmp1+1) % 0xff) ++ retval = 1; ++ else ++ retval = 0; ++ ++ /* Restore register contents. May not be necessary, but do it just to ++ * be safe. */ ++ WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); ++ ++ return retval; ++} ++ ++static const char im_name[] = "DoCMilPlus_init"; ++ ++/* This routine is made available to other mtd code via ++ * inter_module_register. It must only be accessed through ++ * inter_module_get which will bump the use count of this module. The ++ * addresses passed back in mtd are valid as long as the use count of ++ * this module is non-zero, i.e. between inter_module_get and ++ * inter_module_put. Keith Owens 29 Oct 2000. ++ */ ++static void DoCMilPlus_init(struct mtd_info *mtd) ++{ ++ struct DiskOnChip *this = mtd->priv; ++ struct DiskOnChip *old = NULL; ++ ++ /* We must avoid being called twice for the same device. */ ++ if (docmilpluslist) ++ old = docmilpluslist->priv; ++ ++ while (old) { ++ if (DoCMilPlus_is_alias(this, old)) { ++ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " ++ "Plus at 0x%lX - already configured\n", ++ this->physadr); ++ iounmap(this->virtadr); ++ kfree(mtd); ++ return; ++ } ++ if (old->nextdoc) ++ old = old->nextdoc->priv; ++ else ++ old = NULL; ++ } ++ ++ mtd->name = "DiskOnChip Millennium Plus"; ++ printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " ++ "address 0x%lX\n", this->physadr); ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; ++ mtd->size = 0; ++ ++ mtd->erasesize = 0; ++ mtd->oobblock = 512; ++ mtd->oobsize = 16; ++ mtd->owner = THIS_MODULE; ++ mtd->erase = doc_erase; ++ mtd->point = NULL; ++ mtd->unpoint = NULL; ++ mtd->read = doc_read; ++ mtd->write = doc_write; ++ mtd->read_ecc = doc_read_ecc; ++ mtd->write_ecc = doc_write_ecc; ++ mtd->read_oob = doc_read_oob; ++ mtd->write_oob = doc_write_oob; ++ mtd->sync = NULL; ++ ++ this->totlen = 0; ++ this->numchips = 0; ++ this->curfloor = -1; ++ this->curchip = -1; ++ ++ /* Ident all the chips present. */ ++ DoC_ScanChips(this); ++ ++ if (!this->totlen) { ++ kfree(mtd); ++ iounmap(this->virtadr); ++ } else { ++ this->nextdoc = docmilpluslist; ++ docmilpluslist = mtd; ++ mtd->size = this->totlen; ++ mtd->erasesize = this->erasesize; ++ add_mtd_device(mtd); ++ return; ++ } ++} ++ ++#if 0 ++static int doc_dumpblk(struct mtd_info *mtd, loff_t from) ++{ ++ int i; ++ loff_t fofs; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ unsigned char *bp, buf[1056]; ++ char c[32]; ++ ++ from &= ~0x3ff; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, 1054); ++ buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ memset(&c[0], 0, sizeof(c)); ++ printk("DUMP OFFSET=%x:\n", (int)from); ++ ++ for (i = 0, bp = &buf[0]; (i < 1056); i++) { ++ if ((i % 16) == 0) ++ printk("%08x: ", i); ++ printk(" %02x", *bp); ++ c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; ++ bp++; ++ if (((i + 1) % 16) == 0) ++ printk(" %s\n", c); ++ } ++ printk("\n"); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return 0; ++} ++#endif ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ /* Just a special case of doc_read_ecc */ ++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); ++} ++ ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int ret, i; ++ volatile char dummy; ++ loff_t fofs; ++ unsigned char syndrome[6]; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow a single read to cross a 512-byte block boundary */ ++ if (from + len > ((from | 0x1ff) + 1)) ++ len = ((from | 0x1ff) + 1) - from; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ if (eccbuf) { ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); ++ } else { ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ } ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ret = 0; ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ if (eccbuf) { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len); ++ ++ /* Read the ECC data following raw data */ ++ MemReadDOC(docptr, eccbuf, 4); ++ eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); ++ eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Flush the pipeline */ ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ ++ /* Check the ECC Status */ ++ if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { ++ int nb_errors; ++ /* There was an ECC error */ ++#ifdef ECC_DEBUG ++ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); ++#endif ++ /* Read the ECC syndrom through the DiskOnChip ECC logic. ++ These syndrome will be all ZERO when there is no error */ ++ for (i = 0; i < 6; i++) ++ syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ nb_errors = doc_decode_ecc(buf, syndrome); ++#ifdef ECC_DEBUG ++ printk("ECC Errors corrected: %x\n", nb_errors); ++#endif ++ if (nb_errors < 0) { ++ /* We return error, but have actually done the read. Not that ++ this can be told to user-space, via sys_read(), but at least ++ MTD-aware stuff can know about it by checking *retlen */ ++#ifdef ECC_DEBUG ++ printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", ++ __FILE__, __LINE__, (int)from); ++ printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ syndrome[0], syndrome[1], syndrome[2], ++ syndrome[3], syndrome[4], syndrome[5]); ++ printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ eccbuf[0], eccbuf[1], eccbuf[2], ++ eccbuf[3], eccbuf[4], eccbuf[5]); ++#endif ++ ret = -EIO; ++ } ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); ++ } else { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len-2); ++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return ret; ++} ++ ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ char eccbuf[6]; ++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); ++} ++ ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int i, before, ret = 0; ++ loff_t fto; ++ volatile char dummy; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[to >> (this->chipshift)]; ++ ++ /* Don't allow write past end of device */ ++ if (to >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow writes which aren't exactly one block (512 bytes) */ ++ if ((to & 0x1ff) || (len != 0x200)) ++ return -EINVAL; ++ ++ /* Determine position of OOB flags, before or after data */ ++ before = (this->interleave && (to & 0x200)); ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Set device to appropriate plane of flash */ ++ fto = to; ++ WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); ++ ++ /* On interleaved devices the flags for 2nd half 512 are before data */ ++ if (eccbuf && before) ++ fto -= 2; ++ ++ /* issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fto, 0x00, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ if (eccbuf) { ++ if (before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ } ++ ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); ++ } ++ ++ MemWriteDOC(docptr, (unsigned char *) buf, len); ++ ++ if (eccbuf) { ++ /* Write ECC data to flash, the ECC info is generated by ++ the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ ++ DoC_Delay(docptr, 3); ++ ++ /* Read the ECC data through the DiskOnChip ECC logic */ ++ for (i = 0; i < 6; i++) ++ eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ ++ /* Write the ECC data to flash */ ++ MemWriteDOC(docptr, eccbuf, 6); ++ ++ if (!before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+6); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+7); ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); ++ /* Error in programming ++ FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ++ return ret; ++} ++ ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ loff_t fofs, base; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ DoC_WaitReady(docptr); ++ ++ /* Maximum of 16 bytes in the OOB region, so limit read to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0xf; ++ if (!this->interleave) { ++ DoC_Command(docptr, NAND_CMD_READOOB, 0); ++ size = 16 - base; ++ } else if (base < 6) { ++ DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); ++ size = 6 - base; ++ } else if (base < 8) { ++ DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); ++ size = 8 - base; ++ } else { ++ DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue read command */ ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ MemReadDOC(docptr, &buf[got], size - 2); ++ buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return 0; ++} ++ ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ volatile char dummy; ++ loff_t fofs, base; ++ struct DiskOnChip *this = mtd->priv; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ int ret = 0; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ ++ /* Maximum of 16 bytes in the OOB region, so limit write to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0x0f; ++ if (!this->interleave) { ++ WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } else if (base < 6) { ++ WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 6 - base; ++ } else if (base < 8) { ++ WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 8 - base; ++ } else { ++ WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ /* Write the data via the internal pipeline through CDSN IO ++ register, see Pipelined Write Operations 11.2 */ ++ MemWriteDOC(docptr, (unsigned char *) &buf[got], size); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0x00); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming oob at 0x%x\n", ++ dummy, (int)ofs); ++ /* FIXME: implement Bad Block Replacement */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return ret; ++} ++ ++int doc_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ volatile char dummy; ++ struct DiskOnChip *this = mtd->priv; ++ __u32 ofs = instr->addr; ++ __u32 len = instr->len; ++ void __iomem * docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ ++ DoC_CheckASIC(docptr); ++ ++ if (len != mtd->erasesize) ++ printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", ++ len, mtd->erasesize); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ instr->state = MTD_ERASE_PENDING; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ DoC_Command(docptr, NAND_CMD_RESET, 0x00); ++ DoC_WaitReady(docptr); ++ ++ DoC_Command(docptr, NAND_CMD_ERASE1, 0); ++ DoC_Address(this, 2, ofs, 0, 0x00); ++ DoC_Command(docptr, NAND_CMD_ERASE2, 0); ++ DoC_WaitReady(docptr); ++ instr->state = MTD_ERASING; ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5. */ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); ++ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ mtd_erase_callback(instr); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ * Module stuff ++ * ++ ****************************************************************************/ ++ ++static int __init init_doc2001plus(void) ++{ ++ inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); ++ return 0; ++} ++ ++static void __exit cleanup_doc2001plus(void) ++{ ++ struct mtd_info *mtd; ++ struct DiskOnChip *this; ++ ++ while ((mtd=docmilpluslist)) { ++ this = mtd->priv; ++ docmilpluslist = this->nextdoc; ++ ++ del_mtd_device(mtd); ++ ++ iounmap(this->virtadr); ++ kfree(this->chips); ++ kfree(mtd); ++ } ++ inter_module_unregister(im_name); ++} ++ ++module_exit(cleanup_doc2001plus); ++module_init(init_doc2001plus); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer et al."); ++MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); +--- linux-2.4.21/drivers/mtd/devices/docecc.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/docecc.c +@@ -7,7 +7,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -519,6 +519,8 @@ + return nb_errors; + } + ++EXPORT_SYMBOL_GPL(doc_decode_ecc); ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Fabrice Bellard "); + MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); +--- linux-2.4.21/drivers/mtd/devices/docprobe.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/docprobe.c +@@ -4,7 +4,7 @@ + /* (C) 1999 Machine Vision Holdings, Inc. */ + /* (C) 1999-2003 David Woodhouse */ + +-/* $Id$ */ ++/* $Id$ */ + + + +@@ -31,14 +31,12 @@ + /* DOC_SINGLE_DRIVER: + Millennium driver has been merged into DOC2000 driver. + +- The newly-merged driver doesn't appear to work for writing. It's the +- same with the DiskOnChip 2000 and the Millennium. If you have a +- Millennium and you want write support to work, remove the definition +- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver. +- +- Otherwise, it's left on in the hope that it'll annoy someone with +- a Millennium enough that they go through and work out what the +- difference is :) ++ The old Millennium-only driver has been retained just in case there ++ are problems with the new code. If the combined driver doesn't work ++ for you, you can try the old one by undefining DOC_SINGLE_DRIVER ++ below and also enabling it in your configuration. If this fixes the ++ problems, please send a report to the MTD mailing list at ++ . + */ + #define DOC_SINGLE_DRIVER + +@@ -47,18 +45,15 @@ + #include + #include + #include +-#include +-#include +-#include + #include + #include +-#include + #include + #include + + #include + #include + #include ++#include + + /* Where to look for the devices? */ + #ifndef CONFIG_MTD_DOCPROBE_ADDRESS +@@ -95,14 +90,14 @@ + ##else + #warning Unknown architecture for DiskOnChip. No default probe locations defined + #endif +- 0 }; ++ 0xffffffff }; + + /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ + +-static inline int __init doccheck(unsigned long potential, unsigned long physadr) ++static inline int __init doccheck(void __iomem *potential, unsigned long physadr) + { +- unsigned long window=potential; +- unsigned char tmp, ChipID; ++ void __iomem *window=potential; ++ unsigned char tmp, tmpb, tmpc, ChipID; + #ifndef DOC_PASSIVE_PROBE + unsigned char tmp2; + #endif +@@ -140,26 +135,80 @@ + window, DOCControl); + #endif /* !DOC_PASSIVE_PROBE */ + ++ /* We need to read the ChipID register four times. For some ++ newer DiskOnChip 2000 units, the first three reads will ++ return the DiskOnChip Millennium ident. Don't ask. */ + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; + break; + + case DOC_ChipID_DocMil: ++ /* Check for the new 2000 with Millennium ASIC */ ++ ReadDOC(window, ChipID); ++ ReadDOC(window, ChipID); ++ if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) ++ ChipID = DOC_ChipID_Doc2kTSOP; ++ + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; + break; + ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ case 0: ++ /* Possible Millennium+, need to do more checks */ ++#ifndef DOC_PASSIVE_PROBE ++ /* Possibly release from power down mode */ ++ for (tmp = 0; (tmp < 4); tmp++) ++ ReadDOC(window, Mplus_Power); ++ ++ /* Reset the DiskOnChip ASIC */ ++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ ++ mdelay(1); ++ /* Enable the DiskOnChip ASIC */ ++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ mdelay(1); ++#endif /* !DOC_PASSIVE_PROBE */ ++ ++ ChipID = ReadDOC(window, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ /* Check the TOGGLE bit in the toggle register */ ++ tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) ++ return ChipID; + default: +-#ifndef CONFIG_MTD_DOCPROBE_55AA +- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ++ break; ++ } ++ /* FALL TRHU */ ++ ++ default: ++ ++#ifdef CONFIG_MTD_DOCPROBE_55AA ++ printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + ChipID, physadr); + #endif + #ifndef DOC_PASSIVE_PROBE +@@ -184,7 +233,7 @@ + + static void __init DoC_Probe(unsigned long physadr) + { +- unsigned long docptr; ++ void __iomem *docptr; + struct DiskOnChip *this; + struct mtd_info *mtd; + int ChipID; +@@ -194,18 +243,24 @@ + char *im_modname = NULL; + void (*initroutine)(struct mtd_info *) = NULL; + +- docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); ++ docptr = ioremap(physadr, DOC_IOREMAP_LEN); + + if (!docptr) + return; + + if ((ChipID = doccheck(docptr, physadr))) { ++ if (ChipID == DOC_ChipID_Doc2kTSOP) { ++ /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ ++ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); ++ iounmap(docptr); ++ return; ++ } + docfound = 1; + mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); + + if (!mtd) { + printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); +- iounmap((void *)docptr); ++ iounmap(docptr); + return; + } + +@@ -221,6 +276,12 @@ + sprintf(namebuf, "with ChipID %2.2X", ChipID); + + switch(ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ name="2000 TSOP"; ++ im_funcname = "DoC2k_init"; ++ im_modname = "doc2000"; ++ break; ++ + case DOC_ChipID_Doc2k: + name="2000"; + im_funcname = "DoC2k_init"; +@@ -237,6 +298,13 @@ + im_modname = "doc2001"; + #endif /* DOC_SINGLE_DRIVER */ + break; ++ ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ name="MillenniumPlus"; ++ im_funcname = "DoCMilPlus_init"; ++ im_modname = "doc2001plus"; ++ break; + } + + if (im_funcname) +@@ -248,8 +316,9 @@ + return; + } + printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); ++ kfree(mtd); + } +- iounmap((void *)docptr); ++ iounmap(docptr); + } + + +@@ -259,7 +328,7 @@ + * + ****************************************************************************/ + +-int __init init_doc(void) ++static int __init init_doc(void) + { + int i; + +@@ -267,7 +336,7 @@ + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + DoC_Probe(doc_config_location); + } else { +- for (i=0; doc_locations[i]; i++) { ++ for (i=0; (doc_locations[i] != 0xffffffff); i++) { + DoC_Probe(doc_locations[i]); + } + } +@@ -275,11 +344,7 @@ + found, so the user knows we at least tried. */ + if (!docfound) + printk(KERN_INFO "No recognised DiskOnChip devices found\n"); +- /* So it looks like we've been used and we get unloaded */ +- MOD_INC_USE_COUNT; +- MOD_DEC_USE_COUNT; +- return 0; +- ++ return -EAGAIN; + } + + module_init(init_doc); +--- linux-2.4.21/drivers/mtd/devices/lart.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/lart.c +@@ -2,7 +2,7 @@ + /* + * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. + * +- * $Id$ ++ * $Id$ + * + * Author: Abraham vd Merwe + * +@@ -42,7 +42,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #ifdef HAVE_PARTITIONS +@@ -433,7 +433,7 @@ + } + + instr->state = MTD_ERASE_DONE; +- if (instr->callback) instr->callback (instr); ++ mtd_erase_callback(instr); + + return (0); + } +@@ -584,45 +584,40 @@ + + static struct mtd_info mtd; + +-static struct mtd_erase_region_info erase_regions[] = +-{ ++static struct mtd_erase_region_info erase_regions[] = { + /* parameter blocks */ + { +- offset: 0x00000000, +- erasesize: FLASH_BLOCKSIZE_PARAM, +- numblocks: FLASH_NUMBLOCKS_16m_PARAM ++ .offset = 0x00000000, ++ .erasesize = FLASH_BLOCKSIZE_PARAM, ++ .numblocks = FLASH_NUMBLOCKS_16m_PARAM, + }, + /* main blocks */ + { +- offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, +- erasesize: FLASH_BLOCKSIZE_MAIN, +- numblocks: FLASH_NUMBLOCKS_16m_MAIN ++ .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, ++ .erasesize = FLASH_BLOCKSIZE_MAIN, ++ .numblocks = FLASH_NUMBLOCKS_16m_MAIN, + } + }; + + #ifdef HAVE_PARTITIONS +-static struct mtd_partition lart_partitions[] = +-{ ++static struct mtd_partition lart_partitions[] = { + /* blob */ + { +- name: "blob", +- offset: BLOB_START, +- size: BLOB_LEN, +- mask_flags: 0 ++ .name = "blob", ++ .offset = BLOB_START, ++ .size = BLOB_LEN, + }, + /* kernel */ + { +- name: "kernel", +- offset: KERNEL_START, /* MTDPART_OFS_APPEND */ +- size: KERNEL_LEN, +- mask_flags: 0 ++ .name = "kernel", ++ .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ ++ .size = KERNEL_LEN, + }, + /* initial ramdisk / file system */ + { +- name: "file system", +- offset: INITRD_START, /* MTDPART_OFS_APPEND */ +- size: INITRD_LEN, /* MTDPART_SIZ_FULL */ +- mask_flags: 0 ++ .name = "file system", ++ .offset = INITRD_START, /* MTDPART_OFS_APPEND */ ++ .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ + } + }; + #endif +@@ -646,10 +641,10 @@ + mtd.erasesize = FLASH_BLOCKSIZE_MAIN; + mtd.numeraseregions = NB_OF (erase_regions); + mtd.eraseregions = erase_regions; +- mtd.module = THIS_MODULE; + mtd.erase = flash_erase; + mtd.read = flash_read; + mtd.write = flash_write; ++ mtd.owner = THIS_MODULE; + + #ifdef LART_DEBUG + printk (KERN_DEBUG +--- linux-2.4.21/drivers/mtd/devices/ms02-nv.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/ms02-nv.c +@@ -6,7 +6,7 @@ + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -31,16 +31,16 @@ + static char version[] __initdata = + "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; + +-MODULE_AUTHOR("Maciej W. Rozycki "); ++MODULE_AUTHOR("Maciej W. Rozycki "); + MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); + MODULE_LICENSE("GPL"); + + + /* + * Addresses we probe for an MS02-NV at. Modules may be located +- * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB +- * boundary within a 0MB up to 448MB range. We don't support a module +- * at 0MB, though. ++ * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB ++ * boundary within a 0MiB up to 448MiB range. We don't support a module ++ * at 0MiB, though. + */ + static ulong ms02nv_addrs[] __initdata = { + 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000, +@@ -59,7 +59,7 @@ + static int ms02nv_read(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) + { +- struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; ++ struct ms02nv_private *mp = mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; +@@ -73,7 +73,7 @@ + static int ms02nv_write(struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, const u_char *buf) + { +- struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; ++ struct ms02nv_private *mp = mtd->priv; + + if (to + len > mtd->size) + return -EINVAL; +@@ -130,7 +130,7 @@ + + int ret = -ENODEV; + +- /* The module decodes 8MB of address space. */ ++ /* The module decodes 8MiB of address space. */ + mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); + if (!mod_res) + return -ENOMEM; +@@ -222,7 +222,7 @@ + mtd->flags = MTD_CAP_RAM | MTD_XIP; + mtd->size = fixsize; + mtd->name = (char *)ms02nv_name; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->read = ms02nv_read; + mtd->write = ms02nv_write; + +@@ -233,7 +233,7 @@ + goto err_out_csr_res; + } + +- printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n", ++ printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n", + mtd->index, ms02nv_name, addr, size >> 20); + + mp->next = root_ms02nv_mtd; +@@ -265,7 +265,7 @@ + static void __exit ms02nv_remove_one(void) + { + struct mtd_info *mtd = root_ms02nv_mtd; +- struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; ++ struct ms02nv_private *mp = mtd->priv; + + root_ms02nv_mtd = mp->next; + +@@ -293,12 +293,12 @@ + + switch (mips_machtype) { + case MACH_DS5000_200: +- csr = (volatile u32 *)KN02_CSR_ADDR; ++ csr = (volatile u32 *)KN02_CSR_BASE; + if (*csr & KN02_CSR_BNK32M) + stride = 2; + break; + case MACH_DS5000_2X0: +- case MACH_DS5000: ++ case MACH_DS5900: + csr = (volatile u32 *)KN03_MCR_BASE; + if (*csr & KN03_MCR_BNK32M) + stride = 2; +--- linux-2.4.21/drivers/mtd/devices/ms02-nv.h~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/ms02-nv.h +@@ -1,32 +1,96 @@ + /* +- * Copyright (c) 2001 Maciej W. Rozycki ++ * Copyright (c) 2001, 2003 Maciej W. Rozycki ++ * ++ * DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for ++ * DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260 ++ * systems. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. ++ * ++ * $Id$ + */ + + #include + #include + ++/* ++ * Addresses are decoded as follows: ++ * ++ * 0x000000 - 0x3fffff SRAM ++ * 0x400000 - 0x7fffff CSR ++ * ++ * Within the SRAM area the following ranges are forced by the system ++ * firmware: ++ * ++ * 0x000000 - 0x0003ff diagnostic area, destroyed upon a reboot ++ * 0x000400 - ENDofRAM storage area, available to operating systems ++ * ++ * but we can't really use the available area right from 0x000400 as ++ * the first word is used by the firmware as a status flag passed ++ * from an operating system. If anything but the valid data magic ++ * ID value is found, the firmware considers the SRAM clean, i.e. ++ * containing no valid data, and disables the battery resulting in ++ * data being erased as soon as power is switched off. So the choice ++ * for the start address of the user-available is 0x001000 which is ++ * nicely page aligned. The area between 0x000404 and 0x000fff may ++ * be used by the driver for own needs. ++ * ++ * The diagnostic area defines two status words to be read by an ++ * operating system, a magic ID to distinguish a MS02-NV board from ++ * anything else and a status information providing results of tests ++ * as well as the size of SRAM available, which can be 1MiB or 2MiB ++ * (that's what the firmware handles; no idea if 2MiB modules ever ++ * existed). ++ * ++ * The firmware only handles the MS02-NV board if installed in the ++ * last (15th) slot, so for any other location the status information ++ * stored in the SRAM cannot be relied upon. But from the hardware ++ * point of view there is no problem using up to 14 such boards in a ++ * system -- only the 1st slot needs to be filled with a DRAM module. ++ * The MS02-NV board is ECC-protected, like other MS02 memory boards. ++ * ++ * The state of the battery as provided by the CSR is reflected on ++ * the two onboard LEDs. When facing the battery side of the board, ++ * with the LEDs at the top left and the battery at the bottom right ++ * (i.e. looking from the back side of the system box), their meaning ++ * is as follows (the system has to be powered on): ++ * ++ * left LED battery disable status: lit = enabled ++ * right LED battery condition status: lit = OK ++ */ ++ + /* MS02-NV iomem register offsets. */ + #define MS02NV_CSR 0x400000 /* control & status register */ + ++/* MS02-NV CSR status bits. */ ++#define MS02NV_CSR_BATT_OK 0x01 /* battery OK */ ++#define MS02NV_CSR_BATT_OFF 0x02 /* battery disabled */ ++ ++ + /* MS02-NV memory offsets. */ + #define MS02NV_DIAG 0x0003f8 /* diagnostic status */ + #define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */ +-#define MS02NV_RAM 0x000400 /* general-purpose RAM start */ ++#define MS02NV_VALID 0x000400 /* valid data magic ID */ ++#define MS02NV_RAM 0x001000 /* user-exposed RAM start */ + +-/* MS02-NV diagnostic status constants. */ +-#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */ +-#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */ ++/* MS02-NV diagnostic status bits. */ ++#define MS02NV_DIAG_TEST 0x01 /* SRAM test done (?) */ ++#define MS02NV_DIAG_RO 0x02 /* SRAM r/o test done */ ++#define MS02NV_DIAG_RW 0x04 /* SRAM r/w test done */ ++#define MS02NV_DIAG_FAIL 0x08 /* SRAM test failed */ ++#define MS02NV_DIAG_SIZE_MASK 0xf0 /* SRAM size mask */ ++#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* SRAM size shift (left) */ + + /* MS02-NV general constants. */ + #define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */ ++#define MS02NV_VALID_ID 0xbd100248 /* valid data magic ID value */ + #define MS02NV_SLOT_SIZE 0x800000 /* size of the address space + decoded by the module */ + ++ + typedef volatile u32 ms02nv_uint; + + struct ms02nv_private { +--- linux-2.4.21/drivers/mtd/devices/mtdram.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/mtdram.c +@@ -1,6 +1,6 @@ + /* + * mtdram - a test mtd device +- * $Id$ ++ * $Id$ + * Author: Alexander Larsson + * + * Copyright (c) 1999 Alexander Larsson +@@ -13,6 +13,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -55,9 +57,8 @@ + memset((char *)mtd->priv + instr->addr, 0xff, instr->len); + + instr->state = MTD_ERASE_DONE; ++ mtd_erase_callback(instr); + +- if (instr->callback) +- (*(instr->callback))(instr); + return 0; + } + +@@ -136,7 +137,7 @@ + mtd->erasesize = MTDRAM_ERASE_SIZE; + mtd->priv = mapped_address; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = ram_erase; + mtd->point = ram_point; + mtd->unpoint = ram_unpoint; +@@ -152,12 +153,12 @@ + + #if CONFIG_MTDRAM_TOTAL_SIZE > 0 + #if CONFIG_MTDRAM_ABS_POS > 0 +-int __init init_mtdram(void) ++static int __init init_mtdram(void) + { + void *addr; + int err; + /* Allocate some memory */ +- mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_info) + return -ENOMEM; + +@@ -185,12 +186,12 @@ + + #else /* CONFIG_MTDRAM_ABS_POS > 0 */ + +-int __init init_mtdram(void) ++static int __init init_mtdram(void) + { + void *addr; + int err; + /* Allocate some memory */ +- mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_info) + return -ENOMEM; + +@@ -219,7 +220,7 @@ + + #else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */ + +-int __init init_mtdram(void) ++static int __init init_mtdram(void) + { + return 0; + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/devices/phram.c +@@ -0,0 +1,299 @@ ++/** ++ * $Id$ ++ * ++ * Copyright (c) ???? Jochen Schäuble ++ * Copyright (c) 2003-2004 Jörn Engel ++ * ++ * Usage: ++ * ++ * one commend line parameter per device, each in the form: ++ * phram=,, ++ * may be up to 63 characters. ++ * and can be octal, decimal or hexadecimal. If followed ++ * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or ++ * gigabytes. ++ * ++ * Example: ++ * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) ++ ++struct phram_mtd_list { ++ struct mtd_info mtd; ++ struct list_head list; ++}; ++ ++static LIST_HEAD(phram_list); ++ ++ ++static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ u_char *start = mtd->priv; ++ ++ if (instr->addr + instr->len > mtd->size) ++ return -EINVAL; ++ ++ memset(start + instr->addr, 0xff, instr->len); ++ ++ /* This'll catch a few races. Free the thing before returning :) ++ * I don't feel at all ashamed. This kind of thing is possible anyway ++ * with flash, but unlikely. ++ */ ++ ++ instr->state = MTD_ERASE_DONE; ++ ++ mtd_erase_callback(instr); ++ ++ return 0; ++} ++ ++static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char **mtdbuf) ++{ ++ u_char *start = mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ *mtdbuf = start + from; ++ *retlen = len; ++ return 0; ++} ++ ++static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, ++ size_t len) ++{ ++} ++ ++static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ u_char *start = mtd->priv; ++ ++ if (from >= mtd->size) ++ return -EINVAL; ++ ++ if (len > mtd->size - from) ++ len = mtd->size - from; ++ ++ memcpy(buf, start + from, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ u_char *start = mtd->priv; ++ ++ if (to >= mtd->size) ++ return -EINVAL; ++ ++ if (len > mtd->size - to) ++ len = mtd->size - to; ++ ++ memcpy(start + to, buf, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++ ++ ++static void unregister_devices(void) ++{ ++ struct phram_mtd_list *this, *safe; ++ ++ list_for_each_entry_safe(this, safe, &phram_list, list) { ++ del_mtd_device(&this->mtd); ++ iounmap(this->mtd.priv); ++ kfree(this); ++ } ++} ++ ++static int register_device(char *name, unsigned long start, unsigned long len) ++{ ++ struct phram_mtd_list *new; ++ int ret = -ENOMEM; ++ ++ new = kmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) ++ goto out0; ++ ++ memset(new, 0, sizeof(*new)); ++ ++ ret = -EIO; ++ new->mtd.priv = ioremap(start, len); ++ if (!new->mtd.priv) { ++ ERROR("ioremap failed\n"); ++ goto out1; ++ } ++ ++ ++ new->mtd.name = name; ++ new->mtd.size = len; ++ new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; ++ new->mtd.erase = phram_erase; ++ new->mtd.point = phram_point; ++ new->mtd.unpoint = phram_unpoint; ++ new->mtd.read = phram_read; ++ new->mtd.write = phram_write; ++ new->mtd.owner = THIS_MODULE; ++ new->mtd.type = MTD_RAM; ++ new->mtd.erasesize = PAGE_SIZE; ++ ++ ret = -EAGAIN; ++ if (add_mtd_device(&new->mtd)) { ++ ERROR("Failed to register new device\n"); ++ goto out2; ++ } ++ ++ list_add_tail(&new->list, &phram_list); ++ return 0; ++ ++out2: ++ iounmap(new->mtd.priv); ++out1: ++ kfree(new); ++out0: ++ return ret; ++} ++ ++static int ustrtoul(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long result = simple_strtoul(cp, endp, base); ++ ++ switch (**endp) { ++ case 'G': ++ result *= 1024; ++ case 'M': ++ result *= 1024; ++ case 'k': ++ result *= 1024; ++ /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ ++ if ((*endp)[1] == 'i') ++ (*endp) += 2; ++ } ++ return result; ++} ++ ++static int parse_num32(uint32_t *num32, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ ++ *num32 = n; ++ return 0; ++} ++ ++static int parse_name(char **pname, const char *token) ++{ ++ size_t len; ++ char *name; ++ ++ len = strlen(token) + 1; ++ if (len > 64) ++ return -ENOSPC; ++ ++ name = kmalloc(len, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ strcpy(name, token); ++ ++ *pname = name; ++ return 0; ++} ++ ++ ++static inline void kill_final_newline(char *str) ++{ ++ char *newline = strrchr(str, '\n'); ++ if (newline && !newline[1]) ++ *newline = 0; ++} ++ ++ ++#define parse_err(fmt, args...) do { \ ++ ERROR(fmt , ## args); \ ++ return 0; \ ++} while (0) ++ ++static int phram_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[64+12+12], *str = buf; ++ char *token[3]; ++ char *name; ++ uint32_t start; ++ uint32_t len; ++ int i, ret; ++ ++ if (strnlen(val, sizeof(buf)) >= sizeof(buf)) ++ parse_err("parameter too long\n"); ++ ++ strcpy(str, val); ++ kill_final_newline(str); ++ ++ for (i=0; i<3; i++) ++ token[i] = strsep(&str, ","); ++ ++ if (str) ++ parse_err("too many arguments\n"); ++ ++ if (!token[2]) ++ parse_err("not enough arguments\n"); ++ ++ ret = parse_name(&name, token[0]); ++ if (ret == -ENOMEM) ++ parse_err("out of memory\n"); ++ if (ret == -ENOSPC) ++ parse_err("name too long\n"); ++ if (ret) ++ return 0; ++ ++ ret = parse_num32(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address\n"); ++ ++ ret = parse_num32(&len, token[2]); ++ if (ret) ++ parse_err("illegal device length\n"); ++ ++ register_device(name, start, len); ++ ++ return 0; ++} ++ ++module_param_call(phram, phram_setup, NULL, NULL, 000); ++MODULE_PARM_DESC(phram,"Memory region to map. \"map=,,\""); ++ ++ ++static int __init init_phram(void) ++{ ++ return 0; ++} ++ ++static void __exit cleanup_phram(void) ++{ ++ unregister_devices(); ++} ++ ++module_init(init_phram); ++module_exit(cleanup_phram); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jörn Engel "); ++MODULE_DESCRIPTION("MTD driver for physical RAM"); +--- linux-2.4.21/drivers/mtd/devices/pmc551.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/pmc551.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * PMC551 PCI Mezzanine Ram Device + * +@@ -82,6 +82,7 @@ + * * Comb the init routine. It's still a bit cludgy on a few things. + */ + ++#include + #include + #include + #include +@@ -108,17 +109,11 @@ + #include + #include + +-#if LINUX_VERSION_CODE > 0x20300 +-#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start) +-#else +-#define PCI_BASE_ADDRESS(dev) (dev->base_address[0]) +-#endif +- + static struct mtd_info *pmc551list; + + static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) + { +- struct mypriv *priv = (struct mypriv *)mtd->priv; ++ struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; +@@ -174,16 +169,14 @@ + printk(KERN_DEBUG "pmc551_erase() done\n"); + #endif + +- if (instr->callback) { +- (*(instr->callback))(instr); +- } ++ mtd_erase_callback(instr); + return 0; + } + + + static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) + { +- struct mypriv *priv = (struct mypriv *)mtd->priv; ++ struct mypriv *priv = mtd->priv; + u32 soff_hi; + u32 soff_lo; + +@@ -224,7 +217,7 @@ + + static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { +- struct mypriv *priv = (struct mypriv *)mtd->priv; ++ struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; +@@ -286,7 +279,7 @@ + + static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- struct mypriv *priv = (struct mypriv *)mtd->priv; ++ struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; +@@ -563,7 +556,7 @@ + (size<1024)?size:(size<1048576)?size>>10:size>>20, + (size<1024)?'B':(size<1048576)?'K':'M', + size, ((dcmd&(0x1<<3)) == 0)?"non-":"", +- PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK ); ++ (dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK ); + + /* + * Check to see the state of the memory +@@ -655,7 +648,7 @@ + /* + * PMC551 Card Initialization + */ +-int __init init_pmc551(void) ++static int __init init_pmc551(void) + { + struct pci_dev *PCI_Device = NULL; + struct mypriv *priv; +@@ -681,11 +674,6 @@ + + printk(KERN_INFO PMC551_VERSION); + +- if(!pci_present()) { +- printk(KERN_NOTICE "pmc551: PCI not enabled.\n"); +- return -ENODEV; +- } +- + /* + * PCU-bus chipset probe. + */ +@@ -698,7 +686,7 @@ + } + + printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%lX\n", +- PCI_BASE_ADDRESS(PCI_Device)); ++ PCI_Device->resource[0].start); + + /* + * The PMC551 device acts VERY weird if you don't init it +@@ -752,7 +740,7 @@ + printk(KERN_NOTICE "pmc551: Using specified aperture size %dM\n", asize>>20); + priv->asize = asize; + } +- priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device) ++ priv->start = ioremap(((PCI_Device->resource[0].start) + & PCI_BASE_ADDRESS_MEM_MASK), + priv->asize); + +@@ -787,10 +775,10 @@ + mtd->write = pmc551_write; + mtd->point = pmc551_point; + mtd->unpoint = pmc551_unpoint; +- mtd->module = THIS_MODULE; + mtd->type = MTD_RAM; + mtd->name = "PMC551 RAM board"; + mtd->erasesize = 0x10000; ++ mtd->owner = THIS_MODULE; + + if (add_mtd_device(mtd)) { + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); +@@ -832,7 +820,7 @@ + struct mypriv *priv; + + while((mtd=pmc551list)) { +- priv = (struct mypriv *)mtd->priv; ++ priv = mtd->priv; + pmc551list = priv->nextpmc551; + + if(priv->start) { +--- linux-2.4.21/drivers/mtd/devices/slram.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/devices/slram.c +@@ -1,6 +1,6 @@ + /*====================================================================== + +- $Id$ ++ $Id$ + + This driver provides a method to access memory not used by the kernel + itself (i.e. if the kernel commandline mem=xxx is used). To actually +@@ -50,6 +50,7 @@ + #include + + #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ ++#define SLRAM_BLK_SZ 0x4000 + + #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) + #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) +@@ -75,13 +76,13 @@ + + static slram_mtd_list_t *slram_mtdlist = NULL; + +-int slram_erase(struct mtd_info *, struct erase_info *); +-int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); +-void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); +-int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int slram_erase(struct mtd_info *, struct erase_info *); ++static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); ++static void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); ++static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); ++static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + +-int slram_erase(struct mtd_info *mtd, struct erase_info *instr) ++static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) + { + slram_priv_t *priv = mtd->priv; + +@@ -98,34 +99,38 @@ + + instr->state = MTD_ERASE_DONE; + +- if (instr->callback) { +- (*(instr->callback))(instr); +- } +- else { +- kfree(instr); +- } ++ mtd_erase_callback(instr); + + return(0); + } + +-int slram_point(struct mtd_info *mtd, loff_t from, size_t len, ++static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf) + { +- slram_priv_t *priv = (slram_priv_t *)mtd->priv; ++ slram_priv_t *priv = mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; + + *mtdbuf = priv->start + from; + *retlen = len; + return(0); + } + +-void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) + { + } + +-int slram_read(struct mtd_info *mtd, loff_t from, size_t len, ++static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { +- slram_priv_t *priv = (slram_priv_t *)mtd->priv; ++ slram_priv_t *priv = mtd->priv; ++ ++ if (from > mtd->size) ++ return -EINVAL; ++ ++ if (from + len > mtd->size) ++ len = mtd->size - from; + + memcpy(buf, priv->start + from, len); + +@@ -133,10 +138,13 @@ + return(0); + } + +-int slram_write(struct mtd_info *mtd, loff_t to, size_t len, ++static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) + { +- slram_priv_t *priv = (slram_priv_t *)mtd->priv; ++ slram_priv_t *priv = mtd->priv; ++ ++ if (to + len > mtd->size) ++ return -EINVAL; + + memcpy(priv->start + to, buf, len); + +@@ -146,7 +154,7 @@ + + /*====================================================================*/ + +-int register_device(char *name, unsigned long start, unsigned long length) ++static int register_device(char *name, unsigned long start, unsigned long length) + { + slram_mtd_list_t **curmtd; + +@@ -166,7 +174,7 @@ + if ((*curmtd)->mtdinfo) { + memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); + (*curmtd)->mtdinfo->priv = +- (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); ++ kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + + if (!(*curmtd)->mtdinfo->priv) { + kfree((*curmtd)->mtdinfo); +@@ -193,15 +201,15 @@ + (*curmtd)->mtdinfo->name = name; + (*curmtd)->mtdinfo->size = length; + (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | +- MTD_WRITEB_WRITEABLE | MTD_VOLATILE; ++ MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM; + (*curmtd)->mtdinfo->erase = slram_erase; + (*curmtd)->mtdinfo->point = slram_point; + (*curmtd)->mtdinfo->unpoint = slram_unpoint; + (*curmtd)->mtdinfo->read = slram_read; + (*curmtd)->mtdinfo->write = slram_write; +- (*curmtd)->mtdinfo->module = THIS_MODULE; ++ (*curmtd)->mtdinfo->owner = THIS_MODULE; + (*curmtd)->mtdinfo->type = MTD_RAM; +- (*curmtd)->mtdinfo->erasesize = 0x0; ++ (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; + + if (add_mtd_device((*curmtd)->mtdinfo)) { + E("slram: Failed to register new device\n"); +@@ -218,7 +226,7 @@ + return(0); + } + +-void unregister_devices(void) ++static void unregister_devices(void) + { + slram_mtd_list_t *nextitem; + +@@ -233,7 +241,7 @@ + } + } + +-unsigned long handle_unit(unsigned long value, char *unit) ++static unsigned long handle_unit(unsigned long value, char *unit) + { + if ((*unit == 'M') || (*unit == 'm')) { + return(value * 1024 * 1024); +@@ -243,7 +251,7 @@ + return(value); + } + +-int parse_cmdline(char *devname, char *szstart, char *szlength) ++static int parse_cmdline(char *devname, char *szstart, char *szlength) + { + char *buffer; + unsigned long devstart; +@@ -266,7 +274,7 @@ + } + T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", + devname, devstart, devlength); +- if ((devstart < 0) || (devlength < 0)) { ++ if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { + E("slram: Illegal start / length parameter.\n"); + return(-EINVAL); + } +@@ -290,7 +298,7 @@ + + #endif + +-int init_slram(void) ++static int init_slram(void) + { + char *devname; + int i; +--- linux-2.4.21/drivers/mtd/ftl.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/ftl.c +@@ -1,5 +1,5 @@ + /* This version ported to the Linux-MTD system by dwmw2@infradead.org +- * $Id$ ++ * $Id$ + * + * Fixes: Arnaldo Carvalho de Melo + * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups +@@ -55,8 +55,8 @@ + contact M-Systems (http://www.m-sys.com) directly. + + ======================================================================*/ ++#include + #include +-#include + #include + /*#define PSYCHO_DEBUG */ + +@@ -68,43 +68,13 @@ + #include + #include + #include +-#include ++#include + #include +- +-#if (LINUX_VERSION_CODE >= 0x20100) + #include +-#endif +-#if (LINUX_VERSION_CODE >= 0x20303) + #include +-#endif ++#include + + #include +-/*====================================================================*/ +-/* Stuff which really ought to be in compatmac.h */ +- +-#if (LINUX_VERSION_CODE < 0x20328) +-#define register_disk(dev, drive, minors, ops, size) \ +- do { (dev)->part[(drive)*(minors)].nr_sects = size; \ +- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ +- resetup_one_dev(dev, drive); } while (0) +-#endif +- +-#if (LINUX_VERSION_CODE < 0x20320) +-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +-#define blk_init_queue(q, req) q = (req) +-#define blk_cleanup_queue(q) q = NULL +-#define request_arg_t void +-#else +-#define request_arg_t request_queue_t *q +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif + + /*====================================================================*/ + +@@ -119,19 +89,6 @@ + #define FTL_MAJOR 44 + #endif + +-/* Funky stuff for setting up a block device */ +-#define MAJOR_NR FTL_MAJOR +-#define DEVICE_NAME "ftl" +-#define DEVICE_REQUEST do_ftl_request +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +- +-#define DEVICE_NR(minor) ((minor)>>5) +-#define REGION_NR(minor) (((minor)>>3)&3) +-#define PART_NR(minor) ((minor)&7) +-#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part)) +- +-#include + + /*====================================================================*/ + +@@ -142,8 +99,7 @@ + #define MAX_REGION 4 + + /* Maximum number of partitions in an FTL region */ +-#define PART_BITS 3 +-#define MAX_PART 8 ++#define PART_BITS 4 + + /* Maximum number of outstanding erase requests per socket */ + #define MAX_ERASE 8 +@@ -154,7 +110,7 @@ + + /* Each memory region corresponds to a minor device */ + typedef struct partition_t { +- struct mtd_info *mtd; ++ struct mtd_blktrans_dev mbd; + u_int32_t state; + u_int32_t *VirtualBlockMap; + u_int32_t *VirtualPageMap; +@@ -179,21 +135,10 @@ + region_info_t region; + memory_handle_t handle; + #endif +- atomic_t open; + } partition_t; + +-partition_t *myparts[MAX_MTD_DEVICES]; +- +-static void ftl_notify_add(struct mtd_info *mtd); +-static void ftl_notify_remove(struct mtd_info *mtd); +- + void ftl_freepart(partition_t *part); + +-static struct mtd_notifier ftl_notifier = { +- add: ftl_notify_add, +- remove: ftl_notify_remove, +-}; +- + /* Partition state flags */ + #define FTL_FORMATTED 0x01 + +@@ -204,51 +149,11 @@ + #define XFER_PREPARED 0x03 + #define XFER_FAILED 0x04 + +-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)]; +- +-static struct gendisk ftl_gendisk = { +- major: FTL_MAJOR, +- major_name: "ftl", +- minor_shift: PART_BITS, +- max_p: MAX_PART, +-#if (LINUX_VERSION_CODE < 0x20328) +- max_nr: MAX_DEV*MAX_PART, +-#endif +- part: ftl_hd, +- sizes: ftl_sizes, +-}; +- + /*====================================================================*/ + +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg); +-static int ftl_open(struct inode *inode, struct file *file); +-static release_t ftl_close(struct inode *inode, struct file *file); +-static int ftl_reread_partitions(int minor); + + static void ftl_erase_callback(struct erase_info *done); + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations ftl_blk_fops = { +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +- read: block_read, +- write: block_write, +- fsync: block_fsync +-}; +-#else +-static struct block_device_operations ftl_blk_fops = { +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +-}; +-#endif + + /*====================================================================== + +@@ -262,19 +167,20 @@ + { + erase_unit_header_t header; + loff_t offset, max_offset; +- int ret; ++ size_t ret; ++ int err; + part->header.FormattedSize = 0; +- max_offset = (0x100000mtd->size)?0x100000:part->mtd->size; ++ max_offset = (0x100000mbd.mtd->size)?0x100000:part->mbd.mtd->size; + /* Search first megabyte for a valid FTL header */ + for (offset = 0; + (offset + sizeof(header)) < max_offset; +- offset += part->mtd->erasesize ? : 0x2000) { ++ offset += part->mbd.mtd->erasesize ? : 0x2000) { + +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, ++ err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, + (unsigned char *)&header); + +- if (ret) +- return ret; ++ if (err) ++ return err; + + if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break; + } +@@ -283,15 +189,15 @@ + printk(KERN_NOTICE "ftl_cs: FTL header not found.\n"); + return -ENOENT; + } +- if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 || ++ if (header.BlockSize != 9 || + (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || + (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { + printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); + return -1; + } +- if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { ++ if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { + printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n", +- 1 << header.EraseUnitSize,part->mtd->erasesize); ++ 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); + return -1; + } + part->header = header; +@@ -326,7 +232,7 @@ + for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { + offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) + << part->header.EraseUnitSize); +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval, + (unsigned char *)&header); + + if (ret) +@@ -391,7 +297,7 @@ + part->EUNInfo[i].Deleted = 0; + offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), &retval, + (unsigned char *)part->bam_cache); + +@@ -451,12 +357,13 @@ + if (!erase) + return -ENOMEM; + ++ erase->mtd = part->mbd.mtd; + erase->callback = ftl_erase_callback; + erase->addr = xfer->Offset; + erase->len = 1 << part->header.EraseUnitSize; + erase->priv = (u_long)part; + +- ret = part->mtd->erase(part->mtd, erase); ++ ret = part->mbd.mtd->erase(part->mbd.mtd, erase); + + if (!ret) + xfer->EraseCount++; +@@ -523,7 +430,7 @@ + header.LogicalEUN = cpu_to_le16(0xffff); + header.EraseCount = cpu_to_le32(xfer->EraseCount); + +- ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header), + &retlen, (u_char *)&header); + + if (ret) { +@@ -539,7 +446,7 @@ + + for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&ctl); + + if (ret) +@@ -586,7 +493,7 @@ + + offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); + +@@ -604,7 +511,7 @@ + offset = xfer->Offset + 20; /* Bad! */ + unit = cpu_to_le16(0x7fff); + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), + &retlen, (u_char *) &unit); + + if (ret) { +@@ -624,7 +531,7 @@ + break; + case BLOCK_DATA: + case BLOCK_REPLACEMENT: +- ret = part->mtd->read(part->mtd, src, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n"); +@@ -632,7 +539,7 @@ + } + + +- ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE, ++ ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n"); +@@ -651,7 +558,7 @@ + } + + /* Write the BAM to the transfer unit */ +- ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(int32_t), &retlen, + (u_char *)part->bam_cache); + if (ret) { +@@ -661,7 +568,7 @@ + + + /* All clear? Then update the LogicalEUN again */ +- ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), + &retlen, (u_char *)&srcunitswap); + + if (ret) { +@@ -749,8 +656,8 @@ + if (queued) { + DEBUG(1, "ftl_cs: waiting for transfer " + "unit to be prepared...\n"); +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); ++ if (part->mbd.mtd->sync) ++ part->mbd.mtd->sync(part->mbd.mtd); + } else { + static int ne = 0; + if (++ne < 5) +@@ -848,7 +755,7 @@ + /* Invalidate cache */ + part->bam_index = 0xffff; + +- ret = part->mtd->read(part->mtd, ++ ret = part->mbd.mtd->read(part->mbd.mtd, + part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); +@@ -877,78 +784,6 @@ + + } /* find_free */ + +-/*====================================================================== +- +- This gets a memory handle for the region corresponding to the +- minor device number. +- +-======================================================================*/ +- +-static int ftl_open(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *partition; +- +- if (minor>>4 >= MAX_MTD_DEVICES) +- return -ENODEV; +- +- partition = myparts[minor>>4]; +- +- if (!partition) +- return -ENODEV; +- +- if (partition->state != FTL_FORMATTED) +- return -ENXIO; +- +- if (ftl_gendisk.part[minor].nr_sects == 0) +- return -ENXIO; +- +- BLK_INC_USE_COUNT; +- +- if (!get_mtd_device(partition->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { +- put_mtd_device(partition->mtd); +- BLK_DEC_USE_COUNT; +- return -EROFS; +- } +- +- DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); +- +- atomic_inc(&partition->open); +- +- return 0; +-} +- +-/*====================================================================*/ +- +-static release_t ftl_close(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *part = myparts[minor >> 4]; +- int i; +- +- DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); +- +- /* Wait for any pending erase operations to complete */ +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); +- +- for (i = 0; i < part->header.NumTransferUnits; i++) { +- if (part->XferInfo[i].state == XFER_ERASED) +- prepare_xfer(part, i); +- } +- +- atomic_dec(&part->open); +- +- put_mtd_device(part->mtd); +- BLK_DEC_USE_COUNT; +- release_return(0); +-} /* ftl_close */ +- + + /*====================================================================== + +@@ -983,7 +818,7 @@ + else { + offset = (part->EUNInfo[log_addr / bsize].Offset + + (log_addr % bsize)); +- ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE, + &retlen, (u_char *) buffer); + + if (ret) { +@@ -1022,7 +857,7 @@ + le32_to_cpu(part->header.BAMOffset)); + + #ifdef PSYCHO_DEBUG +- ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&old_addr); + if (ret) { + printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); +@@ -1059,7 +894,7 @@ + #endif + part->bam_cache[blk] = le_virt_addr; + } +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&le_virt_addr); + + if (ret) { +@@ -1119,13 +954,13 @@ + part->EUNInfo[part->bam_index].Deleted++; + offset = (part->EUNInfo[part->bam_index].Offset + + blk * SECTOR_SIZE); +- ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, + buffer); + + if (ret) { + printk(KERN_NOTICE "ftl_cs: block write failed!\n"); + printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" +- " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr, ++ " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr, + offset); + return -EIO; + } +@@ -1151,164 +986,32 @@ + return 0; + } /* ftl_write */ + +-/*====================================================================== +- +- IOCTL calls for getting device parameters. +- +-======================================================================*/ +- +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg) ++static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct hd_geometry *geo = (struct hd_geometry *)arg; +- int ret = 0, minor = MINOR(inode->i_rdev); +- partition_t *part= myparts[minor >> 4]; ++ partition_t *part = (void *)dev; + u_long sect; + +- if (!part) +- return -ENODEV; /* How? */ +- +- switch (cmd) { +- case HDIO_GETGEO: +- ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); +- if (ret) return ret; +- /* Sort of arbitrary: round size down to 4K boundary */ ++ /* Sort of arbitrary: round size down to 4KiB boundary */ + sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; +- put_user(1, (char *)&geo->heads); +- put_user(8, (char *)&geo->sectors); +- put_user((sect>>3), (short *)&geo->cylinders); +- put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); +- break; +- case BLKGETSIZE: +- ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); +- break; +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); +- break; +-#endif +- case BLKRRPART: +- ret = ftl_reread_partitions(minor); +- break; +-#if (LINUX_VERSION_CODE < 0x20303) +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- break; +- RO_IOCTLS(inode->i_rdev, arg); +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKFLSBUF: +- ret = blk_ioctl(inode->i_rdev, cmd, arg); +- break; +-#endif +- default: +- ret = -EINVAL; +- } +- +- return ret; +-} /* ftl_ioctl */ +- +-/*====================================================================== +- +- Handler for block device requests + +-======================================================================*/ +- +-static int ftl_reread_partitions(int minor) +-{ +- partition_t *part = myparts[minor >> 4]; +- int i, whole; +- +- DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); +- if ((atomic_read(&part->open) > 1)) { +- return -EBUSY; +- } +- whole = minor & ~(MAX_PART-1); +- +- i = MAX_PART - 1; +- while (i-- > 0) { +- if (ftl_hd[whole+i].nr_sects > 0) { +- kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); +- +- invalidate_device(rdev, 1); +- } +- ftl_hd[whole+i].start_sect = 0; +- ftl_hd[whole+i].nr_sects = 0; +- } +- +- scan_header(part); +- +- register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, +- &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); ++ geo->heads = 1; ++ geo->sectors = 8; ++ geo->cylinders = sect >> 3; + +-#ifdef PCMCIA_DEBUG +- for (i = 0; i < MAX_PART; i++) { +- if (ftl_hd[whole+i].nr_sects > 0) +- printk(KERN_INFO " %d: start %ld size %ld\n", i, +- ftl_hd[whole+i].start_sect, +- ftl_hd[whole+i].nr_sects); +- } +-#endif + return 0; + } + +-/*====================================================================== +- +- Handler for block device requests +- +-======================================================================*/ +- +-static void do_ftl_request(request_arg_t) ++static int ftl_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int ret, minor; +- partition_t *part; +- +- do { +- // sti(); +- INIT_REQUEST; +- +- minor = MINOR(CURRENT->rq_dev); +- +- part = myparts[minor >> 4]; +- if (part) { +- ret = 0; +- +- switch (CURRENT->cmd) { +- case READ: +- ret = ftl_read(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_read returned %d\n", ret); +- break; +- +- case WRITE: +- ret = ftl_write(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_write returned %d\n", ret); +- break; +- +- default: +- panic("ftl_cs: unknown block command!\n"); +- +- } +- } else { +- ret = 1; +- printk("NULL part in ftl_request\n"); +- } +- +- if (!ret) { +- CURRENT->sector += CURRENT->current_nr_sectors; +- } ++ return ftl_read((void *)dev, buf, block, 1); ++} + +- end_request((ret == 0) ? 1 : 0); +- } while (1); +-} /* do_ftl_request */ ++static int ftl_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ return ftl_write((void *)dev, buf, block, 1); ++} + + /*====================================================================*/ + +@@ -1337,19 +1040,9 @@ + + } /* ftl_freepart */ + +-static void ftl_notify_add(struct mtd_info *mtd) ++static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { + partition_t *partition; +- int device; +- +- for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) +- ; +- +- if (device == MAX_MTD_DEVICES) { +- printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" +- "Not scanning <%s>\n", mtd->name); +- return; +- } + + partition = kmalloc(sizeof(partition_t), GFP_KERNEL); + +@@ -1361,92 +1054,57 @@ + + memset(partition, 0, sizeof(partition_t)); + +- partition->mtd = mtd; ++ partition->mbd.mtd = mtd; + + if ((scan_header(partition) == 0) && + (build_maps(partition) == 0)) { + + partition->state = FTL_FORMATTED; +- atomic_set(&partition->open, 0); +- myparts[device] = partition; +- ftl_reread_partitions(device << 4); + #ifdef PCMCIA_DEBUG +- printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", ++ printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n", + le32_to_cpu(partition->header.FormattedSize) >> 10); + #endif +- } else ++ partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; ++ partition->mbd.blksize = SECTOR_SIZE; ++ partition->mbd.tr = tr; ++ partition->mbd.devnum = -1; ++ if (!add_mtd_blktrans_dev((void *)partition)) ++ return; ++ } ++ ++ ftl_freepart(partition); + kfree(partition); + } + +-static void ftl_notify_remove(struct mtd_info *mtd) ++static void ftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i,j; +- +- /* Q: What happens if you try to remove a device which has +- * a currently-open FTL partition on it? +- * +- * A: You don't. The ftl_open routine is responsible for +- * increasing the use count of the driver module which +- * it uses. +- */ +- +- /* That's the theory, anyway :) */ +- +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (myparts[i] && myparts[i]->mtd == mtd) { +- +- if (myparts[i]->state == FTL_FORMATTED) +- ftl_freepart(myparts[i]); +- +- myparts[i]->state = 0; +- for (j=0; j<16; j++) { +- ftl_gendisk.part[j].nr_sects=0; +- ftl_gendisk.part[j].start_sect=0; +- } +- kfree(myparts[i]); +- myparts[i] = NULL; +- } ++ del_mtd_blktrans_dev(dev); ++ ftl_freepart((partition_t *)dev); ++ kfree(dev); + } + ++struct mtd_blktrans_ops ftl_tr = { ++ .name = "ftl", ++ .major = FTL_MAJOR, ++ .part_bits = PART_BITS, ++ .readsect = ftl_readsect, ++ .writesect = ftl_writesect, ++ .getgeo = ftl_getgeo, ++ .add_mtd = ftl_add_mtd, ++ .remove_dev = ftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ + int init_ftl(void) + { +- int i; +- +- memset(myparts, 0, sizeof(myparts)); +- +- DEBUG(0, "$Id$\n"); +- +- if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { +- printk(KERN_NOTICE "ftl_cs: unable to grab major " +- "device number!\n"); +- return -EAGAIN; +- } +- +- for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) +- ftl_blocksizes[i] = 1024; +- for (i = 0; i < MAX_DEV*MAX_PART; i++) { +- ftl_hd[i].nr_sects = 0; +- ftl_hd[i].start_sect = 0; +- } +- blksize_size[FTL_MAJOR] = ftl_blocksizes; +- ftl_gendisk.major = FTL_MAJOR; +- blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); +- add_gendisk(&ftl_gendisk); +- +- register_mtd_user(&ftl_notifier); ++ DEBUG(0, "$Id$\n"); + +- return 0; ++ return register_mtd_blktrans(&ftl_tr); + } + + static void __exit cleanup_ftl(void) + { +- unregister_mtd_user(&ftl_notifier); +- +- unregister_blkdev(FTL_MAJOR, "ftl"); +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); +- blksize_size[FTL_MAJOR] = NULL; +- +- del_gendisk(&ftl_gendisk); ++ deregister_mtd_blktrans(&ftl_tr); + } + + module_init(init_ftl); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/inftlcore.c +@@ -0,0 +1,912 @@ ++/* ++ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) ++ * ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlcore.c code which is: ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * Author: David Woodhouse ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Maximum number of loops while examining next block, to have a ++ * chance to detect consistency problems (they should never happen ++ * because of the checks done in the mounting. ++ */ ++#define MAX_LOOPS 10000 ++ ++extern void INFTL_dumptables(struct INFTLrecord *inftl); ++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); ++ ++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ++{ ++ struct INFTLrecord *inftl; ++ unsigned long temp; ++ ++ if (mtd->type != MTD_NANDFLASH) ++ return; ++ /* OK, this is moderately ugly. But probably safe. Alternatives? */ ++ if (memcmp(mtd->name, "DiskOnChip", 10)) ++ return; ++ ++ if (!mtd->block_isbad) { ++ printk(KERN_ERR ++"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" ++"Please use the new diskonchip driver under the NAND subsystem.\n"); ++ return; ++ } ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); ++ ++ inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); ++ ++ if (!inftl) { ++ printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); ++ return; ++ } ++ memset(inftl, 0, sizeof(*inftl)); ++ ++ inftl->mbd.mtd = mtd; ++ inftl->mbd.devnum = -1; ++ inftl->mbd.blksize = 512; ++ inftl->mbd.tr = tr; ++ memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); ++ inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; ++ ++ if (INFTL_mount(inftl) < 0) { ++ printk(KERN_WARNING "INFTL: could not mount device\n"); ++ kfree(inftl); ++ return; ++ } ++ ++ /* OK, it's a new one. Set up all the data structures. */ ++ ++ /* Calculate geometry */ ++ inftl->cylinders = 1024; ++ inftl->heads = 16; ++ ++ temp = inftl->cylinders * inftl->heads; ++ inftl->sectors = inftl->mbd.size / temp; ++ if (inftl->mbd.size % temp) { ++ inftl->sectors++; ++ temp = inftl->cylinders * inftl->sectors; ++ inftl->heads = inftl->mbd.size / temp; ++ ++ if (inftl->mbd.size % temp) { ++ inftl->heads++; ++ temp = inftl->heads * inftl->sectors; ++ inftl->cylinders = inftl->mbd.size / temp; ++ } ++ } ++ ++ if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "INFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", inftl->mbd.size); ++ printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", ++ inftl->cylinders, inftl->heads , inftl->sectors, ++ (long)inftl->cylinders * (long)inftl->heads * ++ (long)inftl->sectors ); ++ } ++ ++ if (add_mtd_blktrans_dev(&inftl->mbd)) { ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++ return; ++ } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif ++ return; ++} ++ ++static void inftl_remove_dev(struct mtd_blktrans_dev *dev) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++} ++ ++/* ++ * Actual INFTL access routines. ++ */ ++ ++/* ++ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. ++ * This function is used when the give Virtual Unit Chain. ++ */ ++static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate) ++{ ++ u16 pot = inftl->LastFreeEUN; ++ int silly = inftl->nb_blocks; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p," ++ "desperate=%d)\n", inftl, desperate); ++ ++ /* ++ * Normally, we force a fold to happen before we run out of free ++ * blocks completely. ++ */ ++ if (!desperate && inftl->numfreeEUNs < 2) { ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free " ++ "EUNs (%d)\n", inftl->numfreeEUNs); ++ return 0xffff; ++ } ++ ++ /* Scan for a free block */ ++ do { ++ if (inftl->PUtable[pot] == BLOCK_FREE) { ++ inftl->LastFreeEUN = pot; ++ return pot; ++ } ++ ++ if (++pot > inftl->lastEUN) ++ pot = 0; ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: no free blocks found! " ++ "EUN range = %d - %d\n", 0, inftl->LastFreeEUN); ++ return BLOCK_NIL; ++ } ++ } while (pot != inftl->LastFreeEUN); ++ ++ return BLOCK_NIL; ++} ++ ++static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) ++{ ++ u16 BlockMap[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, prevEUN, status; ++ int block, silly; ++ unsigned int targetEUN; ++ struct inftl_oob oob; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d," ++ "pending=%d)\n", inftl, thisVUC, pendingblock); ++ ++ memset(BlockMap, 0xff, sizeof(BlockMap)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = targetEUN = inftl->VUtable[thisVUC]; ++ ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to fold non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ /* ++ * Scan to find the Erase Unit which holds the actual data for each ++ * 512-byte block within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { ++ if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 16 , &retlen, ++ (char *)&oob) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = oob.b.Status | oob.b.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockMap[block] = thisEUN; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: %x\n", ++ block, thisEUN, status); ++ break; ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* ++ * OK. We now know the location of every block in the Virtual Unit ++ * Chain, and the Erase Unit into which we are supposed to be copying. ++ * Go for it. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n", ++ thisVUC, targetEUN); ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { ++ unsigned char movebuf[SECTORSIZE]; ++ int ret; ++ ++ /* ++ * If it's in the target EUN already, or if it's pending write, ++ * do nothing. ++ */ ++ if (BlockMap[block] == targetEUN || (pendingblock == ++ (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { ++ continue; ++ } ++ ++ /* ++ * Copy only in non free block (free blocks can only ++ * happen in case of media errors or deleted blocks). ++ */ ++ if (BlockMap[block] == BLOCK_NIL) ++ continue; ++ ++ ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, ++ &retlen, movebuf); ++ if (ret < 0) { ++ ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), ++ SECTORSIZE, &retlen, movebuf); ++ if (ret != -EIO) ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " ++ "away on retry?\n"); ++ } ++ memset(&oob, 0xff, sizeof(struct inftl_oob)); ++ oob.b.Status = oob.b.Status1 = SECTOR_USED; ++ MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + ++ (block * SECTORSIZE), SECTORSIZE, &retlen, ++ movebuf, (char *)&oob, &inftl->oobinfo); ++ } ++ ++ /* ++ * Newest unit in chain now contains data from _all_ older units. ++ * So go through and erase each unit in chain, oldest first. (This ++ * is important, by doing oldest first if we crash/reboot then it ++ * it is relatively simple to clean up the mess). ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n", ++ thisVUC); ++ ++ for (;;) { ++ /* Find oldest unit in chain. */ ++ thisEUN = inftl->VUtable[thisVUC]; ++ prevEUN = BLOCK_NIL; ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ prevEUN = thisEUN; ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* Check if we are all done */ ++ if (thisEUN == targetEUN) ++ break; ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->PUtable[prevEUN] = BLOCK_NIL; ++ inftl->numfreeEUNs++; ++ } ++ } ++ ++ return targetEUN; ++} ++ ++static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock) ++{ ++ /* ++ * This is the part that needs some cleverness applied. ++ * For now, I'm doing the minimum applicable to actually ++ * get the thing to work. ++ * Wear-levelling and other clever stuff needs to be implemented ++ * and we also need to do some assessment of the results when ++ * the system loses power half-way through the routine. ++ */ ++ u16 LongestChain = 0; ++ u16 ChainLength = 0, thislen; ++ u16 chain, EUN; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p," ++ "pending=%d)\n", inftl, pendingblock); ++ ++ for (chain = 0; chain < inftl->nb_blocks; chain++) { ++ EUN = inftl->VUtable[chain]; ++ thislen = 0; ++ ++ while (EUN <= inftl->lastEUN) { ++ thislen++; ++ EUN = inftl->PUtable[EUN]; ++ if (thislen > 0xff00) { ++ printk(KERN_WARNING "INFTL: endless loop in " ++ "Virtual Chain %d: Unit %x\n", ++ chain, EUN); ++ /* ++ * Actually, don't return failure. ++ * Just ignore this chain and get on with it. ++ */ ++ thislen = 0; ++ break; ++ } ++ } ++ ++ if (thislen > ChainLength) { ++ ChainLength = thislen; ++ LongestChain = chain; ++ } ++ } ++ ++ if (ChainLength < 2) { ++ printk(KERN_WARNING "INFTL: no Virtual Unit Chains available " ++ "for folding. Failing request\n"); ++ return BLOCK_NIL; ++ } ++ ++ return INFTL_foldchain(inftl, LongestChain, pendingblock); ++} ++ ++static int nrbits(unsigned int val, int bitcount) ++{ ++ int i, total = 0; ++ ++ for (i = 0; (i < bitcount); i++) ++ total += (((0x1 << i) & val) ? 1 : 0); ++ return total; ++} ++ ++/* ++ * INFTL_findwriteunit: Return the unit number into which we can write ++ * for this block. Make it available if it isn't already. ++ */ ++static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); ++ unsigned int thisEUN, writeEUN, prev_block, status; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); ++ struct inftl_oob oob; ++ struct inftl_bci bci; ++ unsigned char anac, nacs, parity; ++ size_t retlen; ++ int silly, silly2 = 3; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p," ++ "block=%d)\n", inftl, block); ++ ++ do { ++ /* ++ * Scan the media to find a unit in the VUC which has ++ * a free space for the block in question. ++ */ ++ writeEUN = BLOCK_NIL; ++ thisEUN = inftl->VUtable[thisVUC]; ++ silly = MAX_LOOPS; ++ ++ while (thisEUN <= inftl->lastEUN) { ++ MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci); ++ ++ status = bci.Status | bci.Status1; ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " ++ "EUN %d is %x\n", block , writeEUN, status); ++ ++ switch(status) { ++ case SECTOR_FREE: ++ writeEUN = thisEUN; ++ break; ++ case SECTOR_DELETED: ++ case SECTOR_USED: ++ /* Can't go any further */ ++ goto hitused; ++ case SECTOR_IGNORE: ++ break; ++ default: ++ /* ++ * Invalid block. Don't use it any more. ++ * Must implement. ++ */ ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++ } ++ ++ /* Skip to next block in chain */ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++hitused: ++ if (writeEUN != BLOCK_NIL) ++ return writeEUN; ++ ++ ++ /* ++ * OK. We didn't find one in the existing chain, or there ++ * is no existing chain. Allocate a new one. ++ */ ++ writeEUN = INFTL_findfreeblock(inftl, 0); ++ ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * That didn't work - there were no free blocks just ++ * waiting to be picked up. We're going to have to fold ++ * a chain to make room. ++ */ ++ thisEUN = INFTL_makefreeblock(inftl, 0xffff); ++ ++ /* ++ * Hopefully we free something, lets try again. ++ * This time we are desperate... ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 " ++ "to find free EUN to accommodate write to " ++ "VUC %d\n", thisVUC); ++ writeEUN = INFTL_findfreeblock(inftl, 1); ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * Ouch. This should never happen - we should ++ * always be able to make some room somehow. ++ * If we get here, we've allocated more storage ++ * space than actual media, or our makefreeblock ++ * routine is missing something. ++ */ ++ printk(KERN_WARNING "INFTL: cannot make free " ++ "space.\n"); ++#ifdef DEBUG ++ INFTL_dumptables(inftl); ++ INFTL_dumpVUchains(inftl); ++#endif ++ return BLOCK_NIL; ++ } ++ } ++ ++ /* ++ * Insert new block into virtual chain. Firstly update the ++ * block headers in flash... ++ */ ++ anac = 0; ++ nacs = 0; ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN != BLOCK_NIL) { ++ MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize ++ + 8, 8, &retlen, (char *)&oob.u); ++ anac = oob.u.a.ANAC + 1; ++ nacs = oob.u.a.NACs + 1; ++ } ++ ++ prev_block = inftl->VUtable[thisVUC]; ++ if (prev_block < inftl->nb_blocks) ++ prev_block -= inftl->firstEUN; ++ ++ parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; ++ parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; ++ parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; ++ parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; ++ ++ oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.a.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.a.ANAC = anac; ++ oob.u.a.NACs = nacs; ++ oob.u.a.parityPerField = parity; ++ oob.u.a.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, ++ &retlen, (char *)&oob.u); ++ ++ /* Also back up header... */ ++ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.b.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.b.ANAC = anac; ++ oob.u.b.NACs = nacs; ++ oob.u.b.parityPerField = parity; ++ oob.u.b.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + ++ SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); ++ ++ inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; ++ inftl->VUtable[thisVUC] = writeEUN; ++ ++ inftl->numfreeEUNs--; ++ return writeEUN; ++ ++ } while (silly2--); ++ ++ printk(KERN_WARNING "INFTL: error folding to make room for Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++} ++ ++/* ++ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it. ++ */ ++static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) ++{ ++ unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, status; ++ int block, silly; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p," ++ "thisVUC=%d)\n", inftl, thisVUC); ++ ++ memset(BlockUsed, 0, sizeof(BlockUsed)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to delete non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return; ++ } ++ ++ /* ++ * Scan through the Erase Units to determine whether any data is in ++ * each of the 512-byte blocks within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { ++ if (BlockUsed[block] || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 8 , &retlen, ++ (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockUsed[block] = 1; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) ++ if (BlockUsed[block]) ++ return; ++ ++ /* ++ * For each block in the chain free it and make it available ++ * for future use. Erase from the oldest unit first. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC); ++ ++ for (;;) { ++ u16 *prevEUN = &inftl->VUtable[thisVUC]; ++ thisEUN = *prevEUN; ++ ++ /* If the chain is all gone already, we're done */ ++ if (thisEUN == BLOCK_NIL) { ++ DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); ++ return; ++ } ++ ++ /* Find oldest unit in chain. */ ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ BUG_ON(thisEUN >= inftl->nb_blocks); ++ ++ prevEUN = &inftl->PUtable[thisEUN]; ++ thisEUN = *prevEUN; ++ } ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", ++ thisEUN, thisVUC); ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->numfreeEUNs++; ++ } ++ ++ /* Now sort out whatever was pointing to it... */ ++ *prevEUN = BLOCK_NIL; ++ ++ /* Ideally we'd actually be responsive to new ++ requests while we're doing this -- if there's ++ free space why should others be made to wait? */ ++ cond_resched(); ++ } ++ ++ inftl->VUtable[thisVUC] = BLOCK_NIL; ++} ++ ++static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ size_t retlen; ++ struct inftl_bci bci; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p," ++ "block=%d)\n", inftl, block); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN != BLOCK_NIL) { ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ bci.Status = bci.Status1 = SECTOR_DELETED; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); ++ } ++ return 0; ++} ++ ++static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int writeEUN; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ size_t retlen; ++ struct inftl_oob oob; ++ char *p, *pend; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld," ++ "buffer=%p)\n", inftl, block, buffer); ++ ++ /* Is block all zero? */ ++ pend = buffer + SECTORSIZE; ++ for (p = buffer; p < pend && !*p; p++) ++ ; ++ ++ if (p < pend) { ++ writeEUN = INFTL_findwriteunit(inftl, block); ++ ++ if (writeEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "inftl_writeblock(): cannot find " ++ "block to write to\n"); ++ /* ++ * If we _still_ haven't got a block to use, ++ * we're screwed. ++ */ ++ return 1; ++ } ++ ++ memset(&oob, 0xff, sizeof(struct inftl_oob)); ++ oob.b.Status = oob.b.Status1 = SECTOR_USED; ++ MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + ++ blockofs, SECTORSIZE, &retlen, (char *)buffer, ++ (char *)&oob, &inftl->oobinfo); ++ /* ++ * need to write SECTOR_USED flags since they are not written ++ * in mtd_writeecc ++ */ ++ } else { ++ INFTL_deleteblock(inftl, block); ++ } ++ ++ return 0; ++} ++ ++static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld," ++ "buffer=%p)\n", inftl, block, buffer); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %ld in EUN %d: 0x%04x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%lx\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN == BLOCK_NIL) { ++ /* The requested block is not on the media, return all 0x00 */ ++ memset(buffer, 0, SECTORSIZE); ++ } else { ++ size_t retlen; ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, ++ buffer)) ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ geo->heads = inftl->heads; ++ geo->sectors = inftl->sectors; ++ geo->cylinders = inftl->cylinders; ++ ++ return 0; ++} ++ ++static struct mtd_blktrans_ops inftl_tr = { ++ .name = "inftl", ++ .major = INFTL_MAJOR, ++ .part_bits = INFTL_PARTN_BITS, ++ .getgeo = inftl_getgeo, ++ .readsect = inftl_readblock, ++ .writesect = inftl_writeblock, ++ .add_mtd = inftl_add_mtd, ++ .remove_dev = inftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ ++extern char inftlmountrev[]; ++ ++static int __init init_inftl(void) ++{ ++ printk(KERN_INFO "INFTL: inftlcore.c $Revision$, " ++ "inftlmount.c %s\n", inftlmountrev); ++ ++ return register_mtd_blktrans(&inftl_tr); ++} ++ ++static void __exit cleanup_inftl(void) ++{ ++ deregister_mtd_blktrans(&inftl_tr); ++} ++ ++module_init(init_inftl); ++module_exit(cleanup_inftl); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer , David Woodhouse , Fabrice Bellard et al."); ++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/inftlmount.c +@@ -0,0 +1,804 @@ ++/* ++ * inftlmount.c -- INFTL mount code with extensive checks. ++ * ++ * Author: Greg Ungerer (gerg@snapgear.com) ++ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlmount.c code which is: ++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) ++ * Copyright (C) 2000 Netgem S.A. ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char inftlmountrev[]="$Revision$"; ++ ++/* ++ * find_boot_record: Find the INFTL Media Header and its Spare copy which ++ * contains the various device information of the INFTL partition and ++ * Bad Unit Table. Update the PUtable[] table according to the Bad ++ * Unit Table. PUtable[] is used for management of Erase Unit in ++ * other routines in inftlcore.c and inftlmount.c. ++ */ ++static int find_boot_record(struct INFTLrecord *inftl) ++{ ++ struct inftl_unittail h1; ++ //struct inftl_oob oob; ++ unsigned int i, block; ++ u8 buf[SECTORSIZE]; ++ struct INFTLMediaHeader *mh = &inftl->MediaHdr; ++ struct INFTLPartition *ip; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl); ++ ++ /* ++ * Assume logical EraseSize == physical erasesize for starting the ++ * scan. We'll sort it out later if we find a MediaHeader which says ++ * otherwise. ++ */ ++ inftl->EraseSize = inftl->mbd.mtd->erasesize; ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ ++ inftl->MediaUnit = BLOCK_NIL; ++ ++ /* Search for a valid boot record */ ++ for (block = 0; block < inftl->nb_blocks; block++) { ++ int ret; ++ ++ /* ++ * Check for BNAND header first. Then whinge if it's found ++ * but later checks fail. ++ */ ++ ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, ++ SECTORSIZE, &retlen, buf); ++ /* We ignore ret in case the ECC of the MediaHeader is invalid ++ (which is apparently acceptable) */ ++ if (retlen != SECTORSIZE) { ++ static int warncount = 5; ++ ++ if (warncount) { ++ printk(KERN_WARNING "INFTL: block read at 0x%x " ++ "of mtd%d failed: %d\n", ++ block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ if (!--warncount) ++ printk(KERN_WARNING "INFTL: further " ++ "failures for this block will " ++ "not be printed\n"); ++ } ++ continue; ++ } ++ ++ if (retlen < 6 || memcmp(buf, "BNAND", 6)) { ++ /* BNAND\0 not found. Continue */ ++ continue; ++ } ++ ++ /* To be safer with BIOS, also use erase mark as discriminant */ ++ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + ++ SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { ++ printk(KERN_WARNING "INFTL: ANAND header found at " ++ "0x%x in mtd%d, but OOB data read failed " ++ "(err %d)\n", block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ continue; ++ } ++ ++ ++ /* ++ * This is the first we've seen. ++ * Copy the media header structure into place. ++ */ ++ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); ++ ++ /* Read the spare media header at offset 4096 */ ++ MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, ++ SECTORSIZE, &retlen, buf); ++ if (retlen != SECTORSIZE) { ++ printk(KERN_WARNING "INFTL: Unable to read spare " ++ "Media Header\n"); ++ return -1; ++ } ++ /* Check if this one is the same as the first one we found. */ ++ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { ++ printk(KERN_WARNING "INFTL: Primary and spare Media " ++ "Headers disagree.\n"); ++ return -1; ++ } ++ ++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); ++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); ++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); ++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); ++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); ++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk("INFTL: Media Header ->\n" ++ " bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplerBits = %d\n" ++ " FormatFlgs = %d\n" ++ " OsakVersion = 0x%x\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, mh->NoOfBootImageBlocks, ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->BlockMultiplierBits, mh->FormatFlags, ++ mh->OsakVersion, mh->PercentUsed); ++ } ++#endif ++ ++ if (mh->NoOfBDTLPartitions == 0) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: NoOfBDTLPartitions (%d) == 0, " ++ "must be at least 1\n", mh->NoOfBDTLPartitions); ++ return -1; ++ } ++ ++ if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: Total Partitions (%d) > 4, " ++ "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->NoOfBinaryPartitions); ++ return -1; ++ } ++ ++ if (mh->BlockMultiplierBits > 1) { ++ printk(KERN_WARNING "INFTL: sorry, we don't support " ++ "UnitSizeFactor 0x%02x\n", ++ mh->BlockMultiplierBits); ++ return -1; ++ } else if (mh->BlockMultiplierBits == 1) { ++ printk(KERN_WARNING "INFTL: support for INFTL with " ++ "UnitSizeFactor 0x%02x is experimental\n", ++ mh->BlockMultiplierBits); ++ inftl->EraseSize = inftl->mbd.mtd->erasesize << ++ mh->BlockMultiplierBits; ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ block >>= mh->BlockMultiplierBits; ++ } ++ ++ /* Scan the partitions */ ++ for (i = 0; (i < 4); i++) { ++ ip = &mh->Partitions[i]; ++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits); ++ ip->firstUnit = le32_to_cpu(ip->firstUnit); ++ ip->lastUnit = le32_to_cpu(ip->lastUnit); ++ ip->flags = le32_to_cpu(ip->flags); ++ ip->spareUnits = le32_to_cpu(ip->spareUnits); ++ ip->Reserved0 = le32_to_cpu(ip->Reserved0); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk(" PARTITION[%d] ->\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ i, ip->virtualUnits, ip->firstUnit, ++ ip->lastUnit, ip->flags, ++ ip->spareUnits); ++ } ++#endif ++ ++ if (ip->Reserved0 != ip->firstUnit) { ++ struct erase_info *instr = &inftl->instr; ++ ++ instr->mtd = inftl->mbd.mtd; ++ ++ /* ++ * Most likely this is using the ++ * undocumented qiuck mount feature. ++ * We don't support that, we will need ++ * to erase the hidden block for full ++ * compatibility. ++ */ ++ instr->addr = ip->Reserved0 * inftl->EraseSize; ++ instr->len = inftl->EraseSize; ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ } ++ if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed\n" ++ " firstUnit %d : lastUnit %d > " ++ "virtualUnits %d\n", i, ip->lastUnit, ++ ip->firstUnit, ip->Reserved0); ++ return -1; ++ } ++ if (ip->Reserved1 != 0) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed: " ++ "Reserved1 %d != 0\n", ++ i, ip->Reserved1); ++ return -1; ++ } ++ ++ if (ip->flags & INFTL_BDTL) ++ break; ++ } ++ ++ if (i >= 4) { ++ printk(KERN_WARNING "INFTL: Media Header Partition " ++ "sanity check failed:\n No partition " ++ "marked as Disk Partition\n"); ++ return -1; ++ } ++ ++ inftl->nb_boot_blocks = ip->firstUnit; ++ inftl->numvunits = ip->virtualUnits; ++ if (inftl->numvunits > (inftl->nb_blocks - ++ inftl->nb_boot_blocks - 2)) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed:\n numvunits (%d) > nb_blocks " ++ "(%d) - nb_boot_blocks(%d) - 2\n", ++ inftl->numvunits, inftl->nb_blocks, ++ inftl->nb_boot_blocks); ++ return -1; ++ } ++ ++ inftl->mbd.size = inftl->numvunits * ++ (inftl->EraseSize / SECTORSIZE); ++ ++ /* ++ * Block count is set to last used EUN (we won't need to keep ++ * any meta-data past that point). ++ */ ++ inftl->firstEUN = ip->firstUnit; ++ inftl->lastEUN = ip->lastUnit; ++ inftl->nb_blocks = ip->lastUnit + 1; ++ ++ /* Memory alloc */ ++ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->PUtable) { ++ printk(KERN_WARNING "INFTL: allocation of PUtable " ++ "failed (%zd bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->VUtable) { ++ kfree(inftl->PUtable); ++ printk(KERN_WARNING "INFTL: allocation of VUtable " ++ "failed (%zd bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ /* Mark the blocks before INFTL MediaHeader as reserved */ ++ for (i = 0; i < inftl->nb_boot_blocks; i++) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ /* Mark all remaining blocks as potentially containing data */ ++ for (; i < inftl->nb_blocks; i++) ++ inftl->PUtable[i] = BLOCK_NOTEXPLORED; ++ ++ /* Mark this boot record (NFTL MediaHeader) block as reserved */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ ++ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ ++ for (i = 0; i < inftl->nb_blocks; i++) { ++ int physblock; ++ /* If any of the physical eraseblocks are bad, don't ++ use the unit. */ ++ for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) { ++ if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock)) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ } ++ } ++ ++ inftl->MediaUnit = block; ++ return 0; ++ } ++ ++ /* Not found. */ ++ return -1; ++} ++ ++static int memcmpb(void *a, int c, int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) { ++ if (c != ((unsigned char *)a)[i]) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * check_free_sector: check if a free sector is actually FREE, ++ * i.e. All 0xff in data and oob area. ++ */ ++static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, ++ int len, int check_oob) ++{ ++ u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; ++ size_t retlen; ++ int i; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p," ++ "address=0x%x,len=%d,check_oob=%d)\n", inftl, ++ address, len, check_oob); ++ ++ for (i = 0; i < len; i += SECTORSIZE) { ++ if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0) ++ return -1; ++ if (memcmpb(buf, 0xff, SECTORSIZE) != 0) ++ return -1; ++ ++ if (check_oob) { ++ if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0) ++ return -1; ++ } ++ address += SECTORSIZE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase ++ * Unit and Update INFTL metadata. Each erase operation is ++ * checked with check_free_sectors. ++ * ++ * Return: 0 when succeed, -1 on error. ++ * ++ * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? ++ */ ++int INFTL_formatblock(struct INFTLrecord *inftl, int block) ++{ ++ size_t retlen; ++ struct inftl_unittail uci; ++ struct erase_info *instr = &inftl->instr; ++ int physblock; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," ++ "block=%d)\n", inftl, block); ++ ++ memset(instr, 0, sizeof(struct erase_info)); ++ ++ /* FIXME: Shouldn't we be setting the 'discarded' flag to zero ++ _first_? */ ++ ++ /* Use async erase interface, test return code */ ++ instr->mtd = inftl->mbd.mtd; ++ instr->addr = block * inftl->EraseSize; ++ instr->len = inftl->mbd.mtd->erasesize; ++ /* Erase one physical eraseblock at a time, even though the NAND api ++ allows us to group them. This way we if we have a failure, we can ++ mark only the failed block in the bbt. */ ++ for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ ++ if (instr->state == MTD_ERASE_FAILED) { ++ printk(KERN_WARNING "INFTL: error while formatting block %d\n", ++ block); ++ goto fail; ++ } ++ ++ /* ++ * Check the "freeness" of Erase Unit before updating metadata. ++ * FixMe: is this check really necessary? Since we have check the ++ * return code after the erase operation. ++ */ ++ if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) ++ goto fail; ++ } ++ ++ uci.EraseMark = cpu_to_le16(ERASE_MARK); ++ uci.EraseMark1 = cpu_to_le16(ERASE_MARK); ++ uci.Reserved[0] = 0; ++ uci.Reserved[1] = 0; ++ uci.Reserved[2] = 0; ++ uci.Reserved[3] = 0; ++ instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr + ++ 8, 8, &retlen, (char *)&uci) < 0) ++ goto fail; ++ return 0; ++fail: ++ /* could not format, update the bad block table (caller is responsible ++ for setting the PUtable to BLOCK_RESERVED on failure) */ ++ inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr); ++ return -1; ++} ++ ++/* ++ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase ++ * Units in a Virtual Unit Chain, i.e. all the units are disconnected. ++ * ++ * Since the chain is invalid then we will have to erase it from its ++ * head (normally for INFTL we go from the oldest). But if it has a ++ * loop then there is no oldest... ++ */ ++static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) ++{ ++ unsigned int block = first_block, block1; ++ ++ printk(KERN_WARNING "INFTL: formatting chain at block %d\n", ++ first_block); ++ ++ for (;;) { ++ block1 = inftl->PUtable[block]; ++ ++ printk(KERN_WARNING "INFTL: formatting block %d\n", block); ++ if (INFTL_formatblock(inftl, block) < 0) { ++ /* ++ * Cannot format !!!! Mark it as Bad Unit, ++ */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ } else { ++ inftl->PUtable[block] = BLOCK_FREE; ++ } ++ ++ /* Goto next block on the chain */ ++ block = block1; ++ ++ if (block == BLOCK_NIL || block >= inftl->lastEUN) ++ break; ++ } ++} ++ ++void INFTL_dumptables(struct INFTLrecord *s) ++{ ++ int i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("VUtable[%d] ->", s->nb_blocks); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->VUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks); ++ for (i = 0; i <= s->lastEUN; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->PUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL ->\n" ++ " EraseSize = %d\n" ++ " h/s/c = %d/%d/%d\n" ++ " numvunits = %d\n" ++ " firstEUN = %d\n" ++ " lastEUN = %d\n" ++ " numfreeEUNs = %d\n" ++ " LastFreeEUN = %d\n" ++ " nb_blocks = %d\n" ++ " nb_boot_blocks = %d", ++ s->EraseSize, s->heads, s->sectors, s->cylinders, ++ s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs, ++ s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks); ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++void INFTL_dumpVUchains(struct INFTLrecord *s) ++{ ++ int logical, block, i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL Virtual Unit Chains:\n"); ++ for (logical = 0; logical < s->nb_blocks; logical++) { ++ block = s->VUtable[logical]; ++ if (block > s->nb_blocks) ++ continue; ++ printk(" LOGICAL %d --> %d ", logical, block); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ block = s->PUtable[block]; ++ printk("%d ", block); ++ } ++ printk("\n"); ++ } ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++int INFTL_mount(struct INFTLrecord *s) ++{ ++ unsigned int block, first_block, prev_block, last_block; ++ unsigned int first_logical_block, logical_block, erase_mark; ++ int chain_length, do_format_chain; ++ struct inftl_unithead1 h0; ++ struct inftl_unittail h1; ++ size_t retlen; ++ int i; ++ u8 *ANACtable, ANAC; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s); ++ ++ /* Search for INFTL MediaHeader and Spare INFTL Media Header */ ++ if (find_boot_record(s) < 0) { ++ printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); ++ return -1; ++ } ++ ++ /* Init the logical to physical table */ ++ for (i = 0; i < s->nb_blocks; i++) ++ s->VUtable[i] = BLOCK_NIL; ++ ++ logical_block = block = BLOCK_NIL; ++ ++ /* Temporary buffer to store ANAC numbers. */ ++ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); ++ memset(ANACtable, 0, s->nb_blocks); ++ ++ /* ++ * First pass is to explore each physical unit, and construct the ++ * virtual chains that exist (newest physical unit goes into VUtable). ++ * Any block that is in any way invalid will be left in the ++ * NOTEXPLORED state. Then at the end we will try to format it and ++ * mark it as free. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n"); ++ for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) { ++ if (s->PUtable[first_block] != BLOCK_NOTEXPLORED) ++ continue; ++ ++ do_format_chain = 0; ++ first_logical_block = BLOCK_NIL; ++ last_block = BLOCK_NIL; ++ block = first_block; ++ ++ for (chain_length = 0; ; chain_length++) { ++ ++ if ((chain_length == 0) && ++ (s->PUtable[block] != BLOCK_NOTEXPLORED)) { ++ /* Nothing to do here, onto next block */ ++ break; ++ } ++ ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, ++ 8, &retlen, (char *)&h0) < 0 || ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + ++ 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { ++ /* Should never happen? */ ++ do_format_chain++; ++ break; ++ } ++ ++ logical_block = le16_to_cpu(h0.virtualUnitNo); ++ prev_block = le16_to_cpu(h0.prevUnitNo); ++ erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1)); ++ ANACtable[block] = h0.ANAC; ++ ++ /* Previous block is relative to start of Partition */ ++ if (prev_block < s->nb_blocks) ++ prev_block += s->firstEUN; ++ ++ /* Already explored partial chain? */ ++ if (s->PUtable[block] != BLOCK_NOTEXPLORED) { ++ /* Check if chain for this logical */ ++ if (logical_block == first_logical_block) { ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ } ++ break; ++ } ++ ++ /* Check for invalid block */ ++ if (erase_mark != ERASE_MARK) { ++ printk(KERN_WARNING "INFTL: corrupt block %d " ++ "in chain %d, chain length %d, erase " ++ "mark 0x%x?\n", block, first_block, ++ chain_length, erase_mark); ++ /* ++ * Assume end of chain, probably incomplete ++ * fold/erase... ++ */ ++ if (chain_length == 0) ++ do_format_chain++; ++ break; ++ } ++ ++ /* Check for it being free already then... */ ++ if ((logical_block == BLOCK_FREE) || ++ (logical_block == BLOCK_NIL)) { ++ s->PUtable[block] = BLOCK_FREE; ++ break; ++ } ++ ++ /* Sanity checks on block numbers */ ++ if ((logical_block >= s->nb_blocks) || ++ ((prev_block >= s->nb_blocks) && ++ (prev_block != BLOCK_NIL))) { ++ if (chain_length > 0) { ++ printk(KERN_WARNING "INFTL: corrupt " ++ "block %d in chain %d?\n", ++ block, first_block); ++ do_format_chain++; ++ } ++ break; ++ } ++ ++ if (first_logical_block == BLOCK_NIL) { ++ first_logical_block = logical_block; ++ } else { ++ if (first_logical_block != logical_block) { ++ /* Normal for folded chain... */ ++ break; ++ } ++ } ++ ++ /* ++ * Current block is valid, so if we followed a virtual ++ * chain to get here then we can set the previous ++ * block pointer in our PUtable now. Then move onto ++ * the previous block in the chain. ++ */ ++ s->PUtable[block] = BLOCK_NIL; ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ last_block = block; ++ block = prev_block; ++ ++ /* Check for end of chain */ ++ if (block == BLOCK_NIL) ++ break; ++ ++ /* Validate next block before following it... */ ++ if (block > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid previous " ++ "block %d in chain %d?\n", block, ++ first_block); ++ do_format_chain++; ++ break; ++ } ++ } ++ ++ if (do_format_chain) { ++ format_chain(s, first_block); ++ continue; ++ } ++ ++ /* ++ * Looks like a valid chain then. It may not really be the ++ * newest block in the chain, but it is the newest we have ++ * found so far. We might update it in later iterations of ++ * this loop if we find something newer. ++ */ ++ s->VUtable[first_logical_block] = first_block; ++ logical_block = BLOCK_NIL; ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++#endif ++ ++ /* ++ * Second pass, check for infinite loops in chains. These are ++ * possible because we don't update the previous pointers when ++ * we fold chains. No big deal, just fix them up in PUtable. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n"); ++ for (logical_block = 0; logical_block < s->numvunits; logical_block++) { ++ block = s->VUtable[logical_block]; ++ last_block = BLOCK_NIL; ++ ++ /* Check for free/reserved/nil */ ++ if (block >= BLOCK_RESERVED) ++ continue; ++ ++ ANAC = ANACtable[block]; ++ for (i = 0; i < s->numvunits; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ if (s->PUtable[block] > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid prev %d, " ++ "in virtual chain %d\n", ++ s->PUtable[block], logical_block); ++ s->PUtable[block] = BLOCK_NIL; ++ ++ } ++ if (ANACtable[block] != ANAC) { ++ /* ++ * Chain must point back to itself. This is ok, ++ * but we will need adjust the tables with this ++ * newest block and oldest block. ++ */ ++ s->VUtable[logical_block] = block; ++ s->PUtable[last_block] = BLOCK_NIL; ++ break; ++ } ++ ++ ANAC--; ++ last_block = block; ++ block = s->PUtable[block]; ++ } ++ ++ if (i >= s->nb_blocks) { ++ /* ++ * Uhoo, infinite chain with valid ANACS! ++ * Format whole chain... ++ */ ++ format_chain(s, first_block); ++ } ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumpVUchains(s); ++#endif ++ ++ /* ++ * Third pass, format unreferenced blocks and init free block count. ++ */ ++ s->numfreeEUNs = 0; ++ s->LastFreeEUN = BLOCK_NIL; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n"); ++ for (block = s->firstEUN; block <= s->lastEUN; block++) { ++ if (s->PUtable[block] == BLOCK_NOTEXPLORED) { ++ printk("INFTL: unreferenced block %d, formatting it\n", ++ block); ++ if (INFTL_formatblock(s, block) < 0) ++ s->PUtable[block] = BLOCK_RESERVED; ++ else ++ s->PUtable[block] = BLOCK_FREE; ++ } ++ if (s->PUtable[block] == BLOCK_FREE) { ++ s->numfreeEUNs++; ++ if (s->LastFreeEUN == BLOCK_NIL) ++ s->LastFreeEUN = block; ++ } ++ } ++ ++ kfree(ANACtable); ++ return 0; ++} +--- linux-2.4.21/drivers/mtd/maps/Config.in~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/Config.in +@@ -1,16 +1,18 @@ + # drivers/mtd/maps/Config.in + +-# $Id$ ++# $Id$ + + mainmenu_option next_comment + + comment 'Mapping drivers for chip access' + +-dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE +-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then ++bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS ++ ++bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE ++if [ "$CONFIG_MTD_PHYSMAP" = "y" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 + hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 +- int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 ++ int ' Bank width in octets' CONFIG_MTD_PHYSMAP_BANKWIDTH 2 + fi + + if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then +@@ -21,41 +23,58 @@ + dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS ++ dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT + if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then + hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000 + fi +- dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC ++ dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI + dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE + dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE +- dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE ++ dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE + dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS + dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE + fi + +-if [ "$CONFIG_PPC" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL ++if [ "$CONFIG_PPC32" = "y" ]; then ++ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then ++ dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE ++ fi ++ if [ "$CONFIG_8xx" = "y" ]; then ++ if [ "$CONFIG_TQM8xxL" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then + dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_MBX" = "y" ]; then + dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_DBOX2" = "y" ]; then + dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI ++ fi + dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI +- dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_4xx" = "y" ]; then ++ if [ "$CONFIG_40x" = "y" ]; then ++ if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH ++ dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2 ++ dep_tristate ' Flash device mapped on IBM Walnut' CONFIG_MTD_WALNUT $CONFIG_MTD_JEDECPROBE $CONFIG_WALNUT ++ fi ++ if [ "$CONFIG_440" = "y" ]; then ++ dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_JEDECPROBE $CONFIG_EBONY ++ fi ++ fi + fi + +-if [ "$CONFIG_MIPS" = "y" ]; then +- dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000 +- dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500 +- dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100 +- if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \ +- -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then +- bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT +- bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER +- fi ++if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then ++ dep_tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' CONFIG_MTD_ALCHEMY $CONFIG_SOC_AU1X00 + dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS + if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000 +@@ -63,7 +82,7 @@ + int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2 + fi + dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT +- dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT ++ dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT + fi + + if [ "$CONFIG_SUPERH" = "y" ]; then +@@ -75,22 +94,25 @@ + fi + + if [ "$CONFIG_ARM" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI + dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 + dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE ++ dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 +- dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_FORTUNET +- dep_tristate ' CFI Flash device mapped on Epxa' CONFIG_MTD_EPXA $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT ++ dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK ++ dep_tristate ' CFI Flash device mapped on Ramses board' CONFIG_MTD_RAMSES $CONFIG_MTD_CFI $CONFIG_ARCH_RAMSES $CONFIG_MTD_PARTITIONS ++ dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT ++ dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 +- dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_ARCH_EDB7212 $CONFIG_MTD_CFI ++ dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI ++ dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA ++ dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO + fi + if [ "$CONFIG_ALPHA" = "y" ]; then +- dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE ++ dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS + fi + + if [ "$CONFIG_UCLINUX" = "y" ]; then +@@ -98,7 +120,7 @@ + fi + + # This needs CFI or JEDEC, depending on the cards found. +-dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI +-dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA ++dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS ++dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS + + endmenu +--- linux-2.4.21/drivers/mtd/maps/Makefile~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/Makefile +@@ -1,67 +1,11 @@ + # +-# linux/drivers/maps/Makefile ++# linux/drivers/maps/Makefile.24 ++# Makefile for obsolete kernels + # +-# $Id$ ++# $Id$ + + O_TARGET := mapslink.o ++export-objs := map_funcs.o + +-# Chip mappings +-obj-$(CONFIG_MTD_CDB89712) += cdb89712.o +-obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o +-obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o +-obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o +-obj-$(CONFIG_MTD_DC21285) += dc21285.o +-obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o +-obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o +-obj-$(CONFIG_MTD_EPXA) += epxa-flash.o +-obj-$(CONFIG_MTD_IQ80310) += iq80310.o +-obj-$(CONFIG_MTD_LUBBOCK) += lubbock.o +-obj-$(CONFIG_MTD_PXA_CERF) += pxa_cerf.o +-obj-$(CONFIG_MTD_TRIZEPS2) += trizeps2.o +-obj-$(CONFIG_MTD_L440GX) += l440gx.o +-obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o +-obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o +-obj-$(CONFIG_MTD_MBX860) += mbx860.o +-obj-$(CONFIG_MTD_NORA) += nora.o +-obj-$(CONFIG_MTD_CEIVA) += ceiva.o +-obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o +-ifneq ($(CONFIG_MTD_PHYSMAP),n) +- ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) +- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o +- else +- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +- endif +-endif +-obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +-obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o +-obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o +-obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o +-obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +-ifeq ($(CONFIG_ASSABET_NEPONSET),y) +- obj-$(CONFIG_MTD_SA1100) += neponset-flash.o +-endif +-obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o +-obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o +-obj-$(CONFIG_MTD_NETSC520) += netsc520.o +-obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o +-obj-$(CONFIG_MTD_VMAX) += vmax301.o +-obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o +-obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o +-obj-$(CONFIG_MTD_OCELOT) += ocelot.o +-obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o +-obj-$(CONFIG_MTD_PCI) += pci.o +-obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_LASAT) += lasat.o +-obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o +-obj-$(CONFIG_MTD_EDB7312) += edb7312.o +-obj-$(CONFIG_MTD_IMPA7) += impa7.o +-obj-$(CONFIG_MTD_FORTUNET) += fortunet.o +-obj-$(CONFIG_MTD_REDWOOD) += redwood.o +-obj-$(CONFIG_MTD_UCLINUX) += uclinux.o +-obj-$(CONFIG_MTD_NETtel) += nettel.o +-obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o +- ++include Makefile.common + include $(TOPDIR)/Rules.make +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/Makefile.common +@@ -0,0 +1,73 @@ ++# ++# linux/drivers/maps/Makefile ++# ++# $Id$ ++ ++ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) ++obj-$(CONFIG_MTD) += map_funcs.o ++endif ++ ++# Chip mappings ++obj-$(CONFIG_MTD_CDB89712) += cdb89712.o ++obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o ++obj-$(CONFIG_MTD_BAST) += bast-flash.o ++obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o ++obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o ++obj-$(CONFIG_MTD_DC21285) += dc21285.o ++obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o ++obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o ++obj-$(CONFIG_MTD_IQ80310) += iq80310.o ++obj-$(CONFIG_MTD_L440GX) += l440gx.o ++obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o ++obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o ++obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o ++obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o ++obj-$(CONFIG_MTD_RAMSES) += ramses.o ++obj-$(CONFIG_MTD_MBX860) += mbx860.o ++obj-$(CONFIG_MTD_CEIVA) += ceiva.o ++obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o ++obj-$(CONFIG_MTD_PHYSMAP) += physmap.o ++obj-$(CONFIG_MTD_MULTI_PHYSMAP) += mphysmap.o ++obj-$(CONFIG_MTD_PNC2000) += pnc2000.o ++obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o ++obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o ++obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o ++obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o ++obj-$(CONFIG_MTD_IPAQ) += ipaq-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_VMAX) += vmax301.o ++obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o ++obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o ++obj-$(CONFIG_MTD_OCELOT) += ocelot.o ++obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o ++obj-$(CONFIG_MTD_PCI) += pci.o ++obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o ++obj-$(CONFIG_MTD_LASAT) += lasat.o ++obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o ++obj-$(CONFIG_MTD_EDB7312) += edb7312.o ++obj-$(CONFIG_MTD_IMPA7) += impa7.o ++obj-$(CONFIG_MTD_FORTUNET) += fortunet.o ++obj-$(CONFIG_MTD_REDWOOD) += redwood.o ++obj-$(CONFIG_MTD_CHESTNUT) += chestnut.o ++obj-$(CONFIG_MTD_UCLINUX) += uclinux.o ++obj-$(CONFIG_MTD_NETtel) += nettel.o ++obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o ++obj-$(CONFIG_MTD_EBONY) += ebony.o ++obj-$(CONFIG_MTD_OCOTEA) += ocotea.o ++obj-$(CONFIG_MTD_BEECH) += beech-mtd.o ++obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o ++obj-$(CONFIG_MTD_WALNUT) += walnut.o ++obj-$(CONFIG_MTD_H720X) += h720x-flash.o ++obj-$(CONFIG_MTD_SBC8240) += sbc8240.o ++obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o ++obj-$(CONFIG_MTD_MPC1211) += mpc1211.o ++obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o ++obj-$(CONFIG_MTD_IXP2000) += ixp2000.o ++obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o ++obj-$(CONFIG_MTD_DMV182) += dmv182.o ++obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o ++obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o +--- linux-2.4.21/drivers/mtd/maps/amd76xrom.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/amd76xrom.c +@@ -2,133 +2,138 @@ + * amd76xrom.c + * + * Normal mappings of chips in physical memory +- * $Id$ ++ * $Id$ + */ + + #include + #include + #include ++#include + #include + #include + #include ++#include ++#include + #include + #include + #include ++#include ++ ++ ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define 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; +- unsigned long window_addr; +- u32 window_start, window_size; +- struct pci_dev *pdev; ++ struct resource rsrc; ++ char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; + }; + +-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++static struct amd76xrom_window amd76xrom_window = { ++ .maps = LIST_HEAD_INIT(amd76xrom_window.maps), ++}; + +-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr) ++static void amd76xrom_cleanup(struct amd76xrom_window *window) + { +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} ++ struct amd76xrom_map_info *map, *scratch; ++ u8 byte; + +-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++ 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); ++ } + +-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++ /* Free all of the mtd devices */ ++ list_for_each_entry_safe(map, scratch, &window->maps, list) { ++ if (map->rsrc.parent) { ++ release_resource(&map->rsrc); ++ } ++ del_mtd_device(map->mtd); ++ map_destroy(map->mtd); ++ list_del(&map->list); ++ kfree(map); ++ } ++ if (window->rsrc.parent) ++ release_resource(&window->rsrc); + +-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (window->virt) { ++ iounmap(window->virt); ++ window->virt = NULL; ++ window->phys = 0; ++ window->size = 0; ++ window->pdev = NULL; ++ } + } + +-static struct amd76xrom_map_info amd76xrom_map = { +- map: { +- name: "AMD76X rom", +- size: 0, +- buswidth: 1, +- read8: amd76xrom_read8, +- read16: amd76xrom_read16, +- read32: amd76xrom_read32, +- copy_from: amd76xrom_copy_from, +- write8: amd76xrom_write8, +- write16: amd76xrom_write16, +- write32: amd76xrom_write32, +- copy_to: amd76xrom_copy_to, +- /* The standard rom socket is for single power supply chips +- * that don't have an extra vpp. +- */ +- }, +- mtd: 0, +- window_addr: 0, +-}; + + static int __devinit amd76xrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) + { +- struct rom_window { +- u32 start; +- u32 size; +- u8 segen_bits; +- }; +- static struct rom_window rom_window[] = { +- { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, +- { 0xffc00000, 4*1024*1024, (1<<7), }, +- { 0xffff0000, 64*1024, 0 }, +- { 0 , 0, 0 }, +- }; +- static const u32 rom_probe_sizes[] = { +- 5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, +- 256*1024, 128*1024, 64*1024, 0}; +- static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", 0 }; ++ static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + u8 byte; +- struct amd76xrom_map_info *info = &amd76xrom_map; +- struct rom_window *window; +- int i; +- u32 rom_size; ++ struct amd76xrom_window *window = &amd76xrom_window; ++ struct amd76xrom_map_info *map = NULL; ++ unsigned long map_top; + +- window = &rom_window[0]; +-#if 0 +- while(window->size) { +- if (request_mem_region(window->start, window->size, "amd76xrom")) { +- break; ++ /* Remember the pci dev I find the window in */ ++ window->pdev = pdev; ++ ++ /* 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 */ + } +- window++; ++ else if ((byte & (1<<7)) == (1<<7)) { ++ window->phys = 0xffc00000; /* 4MiB */ + } +- if (!window->size) { +- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n"); +- goto err_out_none; ++ 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" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ window->rsrc.start, window->rsrc.end); + } +-#endif ++ ++#if 0 + + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); +- pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); ++ pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); ++#endif + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, 0x40, &byte); +@@ -136,78 +141,149 @@ + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + +- printk(KERN_NOTICE "amd76xrom window : %x at %x\n", +- window->size, window->start); + /* For write accesses caches are useless */ +- info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size); ++ 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; ++ } + +- if (!info->window_addr) { +- printk(KERN_ERR "Failed to ioremap\n"); +- goto err_out_free_mmio_region; ++ /* 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; + } +- info->mtd = 0; +- for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { +- char **chip_type; +- if (rom_size > window->size) { ++#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, 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; + } +- info->map.map_priv_1 = +- info->window_addr + window->size - rom_size; +- info->map.size = rom_size; +- chip_type = rom_probe_types; +- for(; !info->mtd && *chip_type; chip_type++) { +- info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); + } +- if (info->mtd) { +- break; ++ 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(%u) larger than window(%lu). fixing...\n", ++ 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; + } + } +- if (!info->mtd) { +- goto err_out_iounmap; ++ ++ /* 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; + } +- printk(KERN_NOTICE "amd76xrom chip at offset: %x\n", +- window->size - rom_size); + +- info->mtd->module = THIS_MODULE; +- add_mtd_device(info->mtd); +- info->window_start = window->start; +- info->window_size = window->size; +- return 0; ++ /* Now that the mtd devices is complete claim and export it */ ++ map->mtd->owner = THIS_MODULE; ++ if (add_mtd_device(map->mtd)) { ++ map_destroy(map->mtd); ++ map->mtd = NULL; ++ goto out; ++ } + +-err_out_iounmap: +- iounmap((void *)(info->window_addr)); +-err_out_free_mmio_region: +- release_mem_region(window->start, window->size); +-err_out_none: ++ ++ /* 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 */ ++ if (map) { ++ kfree(map); ++ } ++ /* See if I have any map structures */ ++ if (list_empty(&window->maps)) { ++ amd76xrom_cleanup(window); + return -ENODEV; ++ } ++ return 0; + } + + + static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) + { +- struct amd76xrom_map_info *info = &amd76xrom_map; +- u8 byte; +- +- del_mtd_device(info->mtd); +- map_destroy(info->mtd); +- info->mtd = 0; +- info->map.map_priv_1 = 0; +- +- iounmap((void *)(info->window_addr)); +- info->window_addr = 0; +- +- /* Disable writes through the rom window */ +- pci_read_config_byte(pdev, 0x40, &byte); +- pci_write_config_byte(pdev, 0x40, byte & ~1); ++ struct amd76xrom_window *window = &amd76xrom_window; + +- release_mem_region(info->window_start, info->window_size); ++ amd76xrom_cleanup(window); + } + +-static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = { ++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, } + }; + +@@ -215,26 +291,25 @@ + + #if 0 + static struct pci_driver amd76xrom_driver = { +- name: "amd76xrom", +- id_table: amd76xrom_pci_tbl, +- probe: amd76xrom_init_one, +- remove: amd76xrom_remove_one, ++ .name = MOD_NAME, ++ .id_table = amd76xrom_pci_tbl, ++ .probe = amd76xrom_init_one, ++ .remove = amd76xrom_remove_one, + }; + #endif + +-int __init init_amd76xrom(void) ++static int __init init_amd76xrom(void) + { + struct pci_dev *pdev; + struct pci_device_id *id; +- pdev = 0; ++ pdev = NULL; + for(id = amd76xrom_pci_tbl; id->vendor; id++) { +- pdev = pci_find_device(id->vendor, id->device, 0); ++ pdev = pci_find_device(id->vendor, id->device, NULL); + if (pdev) { + break; + } + } + if (pdev) { +- amd76xrom_map.pdev = pdev; + return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); + } + return -ENXIO; +@@ -245,7 +320,7 @@ + + static void __exit cleanup_amd76xrom(void) + { +- amd76xrom_remove_one(amd76xrom_map.pdev); ++ amd76xrom_remove_one(amd76xrom_window.pdev); + } + + module_init(init_amd76xrom); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/arctic-mtd.c +@@ -0,0 +1,135 @@ ++/* ++ * $Id$ ++ * ++ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Arctic boards. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ * modified for Arctic by, ++ * David Gibson ++ * IBM OzLabs, Canberra, Australia ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) ++ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) ++ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) ++ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) ++ */ ++ ++#define FFS1_SIZE 0x01000000 /* 16MiB */ ++#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ ++#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ ++#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ ++ ++ ++#define NAME "Arctic Linux Flash" ++#define PADDR SUBZERO_BOOTFLASH_PADDR ++#define BUSWIDTH 2 ++#define SIZE SUBZERO_BOOTFLASH_SIZE ++#define PARTITIONS 4 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++{ ++ /* do nothing for now */ ++} ++ ++static struct map_info arctic_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .bankwidth = BUSWIDTH, ++ .phys = PADDR, ++}; ++ ++static struct mtd_info *arctic_mtd; ++ ++static struct mtd_partition arctic_partitions[PARTITIONS] = { ++ { .name = "Filesystem", ++ .size = FFS1_SIZE, ++ .offset = 0,}, ++ { .name = "Kernel", ++ .size = KERNEL_SIZE, ++ .offset = FFS1_SIZE,}, ++ { .name = "Filesystem", ++ .size = FFS2_SIZE, ++ .offset = FFS1_SIZE + KERNEL_SIZE,}, ++ { .name = "Firmware", ++ .size = FIRMWARE_SIZE, ++ .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, ++}; ++ ++static int __init ++init_arctic_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ arctic_mtd_map.virt = ioremap(PADDR, SIZE); ++ ++ if (!arctic_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ simple_map_init(&arctic_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); ++ ++ if (!arctic_mtd) ++ return -ENXIO; ++ ++ arctic_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); ++} ++ ++static void __exit ++cleanup_arctic_mtd(void) ++{ ++ if (arctic_mtd) { ++ del_mtd_partitions(arctic_mtd); ++ map_destroy(arctic_mtd); ++ iounmap((void *) arctic_mtd_map.virt); ++ } ++} ++ ++module_init(init_arctic_mtd); ++module_exit(cleanup_arctic_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Gibson "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); +--- linux-2.4.21/drivers/mtd/maps/autcpu12-nvram.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/autcpu12-nvram.c +@@ -2,7 +2,7 @@ + * NV-RAM memory access on autcpu12 + * (C) 2002 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -32,80 +33,27 @@ + #include + #include + +-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} + + static struct mtd_info *sram_mtd; + + struct map_info autcpu12_sram_map = { +- name: "SRAM", +- size: 32768, +- buswidth: 8, +- read8: autcpu12_read8, +- read16: autcpu12_read16, +- read32: autcpu12_read32, +- copy_from: autcpu12_copy_from, +- write8: autcpu12_write8, +- write16: autcpu12_write16, +- write32: autcpu12_write32, +- copy_to: autcpu12_copy_to ++ .name = "SRAM", ++ .size = 32768, ++ .bankwidth = 4, ++ .phys = 0x12000000, + }; + + static int __init init_autcpu12_sram (void) + { + int err, save0, save1; + +- autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); +- if (!autcpu12_sram_map.map_priv_1) { ++ autcpu12_sram_map.virt = ioremap(0x12000000, SZ_128K); ++ if (!autcpu12_sram_map.virt) { + printk("Failed to ioremap autcpu12 NV-RAM space\n"); + err = -EIO; + goto out; + } ++ simple_map_init(&autcpu_sram_map); + + /* + * Check for 32K/128K +@@ -115,20 +63,20 @@ + * Read and check result on ofs 0x0 + * Restore contents + */ +- save0 = autcpu12_read32(&autcpu12_sram_map,0); +- save1 = autcpu12_read32(&autcpu12_sram_map,0x10000); +- autcpu12_write32(&autcpu12_sram_map,~save0,0x10000); ++ save0 = map_read32(&autcpu12_sram_map,0); ++ save1 = map_read32(&autcpu12_sram_map,0x10000); ++ map_write32(&autcpu12_sram_map,~save0,0x10000); + /* if we find this pattern on 0x0, we have 32K size + * restore contents and exit + */ +- if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) { +- autcpu12_write32(&autcpu12_sram_map,save0,0x0); ++ if ( map_read32(&autcpu12_sram_map,0) != save0) { ++ map_write32(&autcpu12_sram_map,save0,0x0); + goto map; + } + /* We have a 128K found, restore 0x10000 and set size + * to 128K + */ +- autcpu12_write32(&autcpu12_sram_map,save1,0x10000); ++ map_write32(&autcpu12_sram_map,save1,0x10000); + autcpu12_sram_map.size = SZ_128K; + + map: +@@ -139,7 +87,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -148,7 +96,7 @@ + goto out_probe; + } + +- printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); ++ printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + + return 0; + +@@ -157,7 +105,7 @@ + sram_mtd = 0; + + out_ioremap: +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + out: + return err; + } +@@ -167,7 +115,7 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + } + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/bast-flash.c +@@ -0,0 +1,230 @@ ++/* linux/drivers/mtd/maps/bast_flash.c ++ * ++ * Copyright (c) 2004-2005 Simtec Electronics ++ * Ben Dooks ++ * ++ * Simtec Bast (EB2410ITX) NOR MTD Mapping driver ++ * ++ * Changelog: ++ * 20-Sep-2004 BJD Initial version ++ * 17-Jan-2005 BJD Add whole device if no partitions found ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_BAST_MAXSIZE ++#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) ++#else ++#define AREA_MAXSIZE (32 * SZ_1M) ++#endif ++ ++#define PFX "bast-flash: " ++ ++struct bast_flash_info { ++ struct mtd_info *mtd; ++ struct map_info map; ++ struct mtd_partition *partitions; ++ struct resource *area; ++}; ++ ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static struct bast_flash_info *to_bast_info(struct device *dev) ++{ ++ return (struct bast_flash_info *)dev_get_drvdata(dev); ++} ++ ++static void bast_flash_setrw(int to) ++{ ++ unsigned int val; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ val = __raw_readb(BAST_VA_CTRL3); ++ ++ if (to) ++ val |= BAST_CPLD_CTRL3_ROMWEN; ++ else ++ val &= ~BAST_CPLD_CTRL3_ROMWEN; ++ ++ pr_debug("new cpld ctrl3=%02x\n", val); ++ ++ __raw_writeb(val, BAST_VA_CTRL3); ++ local_irq_restore(flags); ++} ++ ++static int bast_flash_remove(struct device *dev) ++{ ++ struct bast_flash_info *info = to_bast_info(dev); ++ ++ dev_set_drvdata(dev, NULL); ++ ++ if (info == NULL) ++ return 0; ++ ++ if (info->map.virt != NULL) ++ iounmap(info->map.virt); ++ ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ ++ if (info->partitions) ++ kfree(info->partitions); ++ ++ if (info->area) { ++ release_resource(info->area); ++ kfree(info->area); ++ } ++ ++ kfree(info); ++ ++ return 0; ++} ++ ++static int bast_flash_probe(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct bast_flash_info *info; ++ struct resource *res; ++ int err = 0; ++ ++ info = kmalloc(sizeof(*info), GFP_KERNEL); ++ if (info == NULL) { ++ printk(KERN_ERR PFX "no memory for flash info\n"); ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ memzero(info, sizeof(*info)); ++ dev_set_drvdata(dev, info); ++ ++ res = pdev->resource; /* assume that the flash has one resource */ ++ ++ info->map.phys = res->start; ++ info->map.size = res->end - res->start + 1; ++ info->map.name = dev->bus_id; ++ info->map.bankwidth = 2; ++ ++ if (info->map.size > AREA_MAXSIZE) ++ info->map.size = AREA_MAXSIZE; ++ ++ pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__, ++ info->map.phys, info->map.size); ++ ++ info->area = request_mem_region(res->start, info->map.size, ++ pdev->name); ++ if (info->area == NULL) { ++ printk(KERN_ERR PFX "cannot reserve flash memory region\n"); ++ err = -ENOENT; ++ goto exit_error; ++ } ++ ++ info->map.virt = ioremap(res->start, info->map.size); ++ pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt); ++ ++ if (info->map.virt == 0) { ++ printk(KERN_ERR PFX "failed to ioremap() region\n"); ++ err = -EIO; ++ goto exit_error; ++ } ++ ++ simple_map_init(&info->map); ++ ++ /* enable the write to the flash area */ ++ ++ bast_flash_setrw(1); ++ ++ /* probe for the device(s) */ ++ ++ info->mtd = do_map_probe("jedec_probe", &info->map); ++ if (info->mtd == NULL) ++ info->mtd = do_map_probe("cfi_probe", &info->map); ++ ++ if (info->mtd == NULL) { ++ printk(KERN_ERR PFX "map_probe() failed\n"); ++ err = -ENXIO; ++ goto exit_error; ++ } ++ ++ /* mark ourselves as the owner */ ++ info->mtd->owner = THIS_MODULE; ++ ++ err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->partitions, err); ++ if (err) ++ printk(KERN_ERR PFX "cannot add/parse partitions\n"); ++ } else { ++ err = add_mtd_device(info->mtd); ++ } ++ ++ if (err == 0) ++ return 0; ++ ++ /* fall through to exit error */ ++ ++ exit_error: ++ bast_flash_remove(dev); ++ return err; ++} ++ ++static struct device_driver bast_flash_driver = { ++ .name = "bast-nor", ++ .bus = &platform_bus_type, ++ .probe = bast_flash_probe, ++ .remove = bast_flash_remove, ++}; ++ ++static int __init bast_flash_init(void) ++{ ++ printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n"); ++ return driver_register(&bast_flash_driver); ++} ++ ++static void __exit bast_flash_exit(void) ++{ ++ driver_unregister(&bast_flash_driver); ++} ++ ++module_init(bast_flash_init); ++module_exit(bast_flash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ben Dooks "); ++MODULE_DESCRIPTION("BAST MTD Map driver"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/beech-mtd.c +@@ -0,0 +1,112 @@ ++/* ++ * $Id$ ++ * ++ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Beech boards. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define NAME "Beech Linux Flash" ++#define PADDR BEECH_BIGFLASH_PADDR ++#define SIZE BEECH_BIGFLASH_SIZE ++#define BUSWIDTH 1 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++ ++static struct map_info beech_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .bankwidth = BUSWIDTH, ++ .phys = PADDR ++}; ++ ++static struct mtd_info *beech_mtd; ++ ++static struct mtd_partition beech_partitions[2] = { ++ { ++ .name = "Linux Kernel", ++ .size = BEECH_KERNEL_SIZE, ++ .offset = BEECH_KERNEL_OFFSET ++ }, { ++ .name = "Free Area", ++ .size = BEECH_FREE_AREA_SIZE, ++ .offset = BEECH_FREE_AREA_OFFSET ++ } ++}; ++ ++static int __init ++init_beech_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ beech_mtd_map.virt = ioremap(PADDR, SIZE); ++ ++ if (!beech_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ ++ simple_map_init(&beech_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); ++ ++ if (!beech_mtd) ++ return -ENXIO; ++ ++ beech_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(beech_mtd, beech_partitions, 2); ++} ++ ++static void __exit ++cleanup_beech_mtd(void) ++{ ++ if (beech_mtd) { ++ del_mtd_partitions(beech_mtd); ++ map_destroy(beech_mtd); ++ iounmap((void *) beech_mtd_map.virt); ++ } ++} ++ ++module_init(init_beech_mtd); ++module_exit(cleanup_beech_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Bishop Brock "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); +--- linux-2.4.21/drivers/mtd/maps/cdb89712.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/cdb89712.c +@@ -1,13 +1,14 @@ + /* + * Flash on Cirrus CDB89712 + * +- * $Id$ ++ * $Id$ + */ + + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -16,77 +17,21 @@ + + + +-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} +- + + static struct mtd_info *flash_mtd; + + struct map_info cdb89712_flash_map = { +- name: "flash", +- size: FLASH_SIZE, +- buswidth: FLASH_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "flash", ++ .size = FLASH_SIZE, ++ .bankwidth = FLASH_WIDTH, ++ .phys = FLASH_START, + }; + + struct resource cdb89712_flash_resource = { +- name: "Flash", +- start: FLASH_START, +- end: FLASH_START + FLASH_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "Flash", ++ .start = FLASH_START, ++ .end = FLASH_START + FLASH_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_flash (void) +@@ -99,13 +44,13 @@ + goto out; + } + +- cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!cdb89712_flash_map.map_priv_1) { ++ cdb89712_flash_map.virt = ioremap(FLASH_START, FLASH_SIZE); ++ if (!cdb89712_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_flash_map); + flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); + if (!flash_mtd) { + flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); +@@ -118,7 +63,7 @@ + goto out_ioremap; + } + +- flash_mtd->module = THIS_MODULE; ++ flash_mtd->owner = THIS_MODULE; + + if (add_mtd_device(flash_mtd)) { + printk("FLASH device addition failed\n"); +@@ -132,7 +77,7 @@ + map_destroy(flash_mtd); + flash_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + out_resource: + release_resource (&cdb89712_flash_resource); + out: +@@ -146,24 +91,17 @@ + static struct mtd_info *sram_mtd; + + struct map_info cdb89712_sram_map = { +- name: "SRAM", +- size: SRAM_SIZE, +- buswidth: SRAM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "SRAM", ++ .size = SRAM_SIZE, ++ .bankwidth = SRAM_WIDTH, ++ .phys = SRAM_START, + }; + + struct resource cdb89712_sram_resource = { +- name: "SRAM", +- start: SRAM_START, +- end: SRAM_START + SRAM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "SRAM", ++ .start = SRAM_START, ++ .end = SRAM_START + SRAM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_sram (void) +@@ -176,13 +114,13 @@ + goto out; + } + +- cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); +- if (!cdb89712_sram_map.map_priv_1) { ++ cdb89712_sram_map.virt = ioremap(SRAM_START, SRAM_SIZE); ++ if (!cdb89712_sram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_sram_map); + sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); + if (!sram_mtd) { + printk("SRAM probe failed\n"); +@@ -190,7 +128,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -205,7 +143,7 @@ + map_destroy(sram_mtd); + sram_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + out_resource: + release_resource (&cdb89712_sram_resource); + out: +@@ -221,20 +159,17 @@ + static struct mtd_info *bootrom_mtd; + + struct map_info cdb89712_bootrom_map = { +- name: "BootROM", +- size: BOOTROM_SIZE, +- buswidth: BOOTROM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, ++ .name = "BootROM", ++ .size = BOOTROM_SIZE, ++ .bankwidth = BOOTROM_WIDTH, ++ .phys = BOOTROM_START, + }; + + struct resource cdb89712_bootrom_resource = { +- name: "BootROM", +- start: BOOTROM_START, +- end: BOOTROM_START + BOOTROM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "BootROM", ++ .start = BOOTROM_START, ++ .end = BOOTROM_START + BOOTROM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_bootrom (void) +@@ -247,13 +182,13 @@ + goto out; + } + +- cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); +- if (!cdb89712_bootrom_map.map_priv_1) { ++ cdb89712_bootrom_map.virt = ioremap(BOOTROM_START, BOOTROM_SIZE); ++ if (!cdb89712_bootrom_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_bootrom_map); + bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); + if (!bootrom_mtd) { + printk("BootROM probe failed\n"); +@@ -261,7 +196,7 @@ + goto out_ioremap; + } + +- bootrom_mtd->module = THIS_MODULE; ++ bootrom_mtd->owner = THIS_MODULE; + bootrom_mtd->erasesize = 0x10000; + + if (add_mtd_device(bootrom_mtd)) { +@@ -276,7 +211,7 @@ + map_destroy(bootrom_mtd); + bootrom_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + out_resource: + release_resource (&cdb89712_bootrom_resource); + out: +@@ -306,21 +241,21 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + release_resource (&cdb89712_sram_resource); + } + + if (flash_mtd) { + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + release_resource (&cdb89712_flash_resource); + } + + if (bootrom_mtd) { + del_mtd_device(bootrom_mtd); + map_destroy(bootrom_mtd); +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + release_resource (&cdb89712_bootrom_resource); + } + } +--- linux-2.4.21/drivers/mtd/maps/ceiva.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/ceiva.c +@@ -11,7 +11,7 @@ + * + * (C) 2000 Nicolas Pitre + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -31,62 +32,10 @@ + #include + + /* +- * This isnt complete yet, so... ++ * This isn't complete yet, so... + */ + #define CONFIG_MTD_CEIVA_STATICMAP + +-static __u8 clps_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 clps_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 clps_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info clps_map __initdata = { +- name: "clps flash", +- read8: clps_read8, +- read16: clps_read16, +- read32: clps_read32, +- copy_from: clps_copy_from, +- write8: clps_write8, +- write16: clps_write16, +- write32: clps_write32, +- copy_to: clps_copy_to, +-}; +- + #ifdef CONFIG_MTD_CEIVA_STATICMAP + /* + * See include/linux/mtd/partitions.h for definition of the mtd_partition +@@ -115,23 +64,23 @@ + + static struct mtd_partition ceiva_partitions[] = { + { +- name: "Ceiva BOOT partition", +- size: BOOT_PARTITION_SIZE_KiB*1024, +- offset: 0, ++ .name = "Ceiva BOOT partition", ++ .size = BOOT_PARTITION_SIZE_KiB*1024, ++ .offset = 0, + + },{ +- name: "Ceiva parameters partition", +- size: PARAMS_PARTITION_SIZE_KiB*1024, +- offset: (16 + 8) * 1024, ++ .name = "Ceiva parameters partition", ++ .size = PARAMS_PARTITION_SIZE_KiB*1024, ++ .offset = (16 + 8) * 1024, + },{ +- name: "Ceiva kernel partition", +- size: (KERNEL_PARTITION_SIZE_KiB)*1024, +- offset: 0x20000, ++ .name = "Ceiva kernel partition", ++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024, ++ .offset = 0x20000, + + },{ +- name: "Ceiva root filesystem partition", +- offset: MTDPART_OFS_APPEND, +- size: (ROOT_PARTITION_SIZE_KiB)*1024, ++ .name = "Ceiva root filesystem partition", ++ .offset = MTDPART_OFS_APPEND, ++ .size = (ROOT_PARTITION_SIZE_KiB)*1024, + } + }; + #endif +@@ -176,7 +125,7 @@ + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; +- ++ memset(maps, 0, sizeof(struct map_info) * nr); + /* + * Claim and then map the memory regions. + */ +@@ -191,7 +140,9 @@ + } + + clps[i].map = maps + i; +- memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); ++ ++ clps[i].map->name = "clps flash"; ++ clps[i].map->phys = clps[i].base; + + clps[i].vbase = ioremap(clps[i].base, clps[i].size); + if (!clps[i].vbase) { +@@ -199,16 +150,18 @@ + break; + } + +- clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; +- clps[i].map->buswidth = clps[i].width; ++ clps[i].map->virt = (void __iomem *)clps[i].vbase; ++ clps[i].map->bankwidth = clps[i].width; + clps[i].map->size = clps[i].size; + ++ simple_map_init(&clps[i].map); ++ + clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); + if (clps[i].mtd == NULL) { + ret = -ENXIO; + break; + } +- clps[i].mtd->module = THIS_MODULE; ++ clps[i].mtd->owner = THIS_MODULE; + subdev[i] = clps[i].mtd; + + printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " +@@ -318,10 +271,8 @@ + return nr; + } + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); +- + static struct mtd_partition *parsed_parts; ++static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; + + static void __init clps_locate_partitions(struct mtd_info *mtd) + { +@@ -331,20 +282,11 @@ + /* + * Partition selection stuff. + */ +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps"); ++ nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); + if (nr_parts > 0) { + part_type = "command line"; + break; + } +-#endif +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mtd, &parsed_parts); +- if (nr_parts > 0) { +- part_type = "RedBoot"; +- break; +- } +-#endif + #ifdef CONFIG_MTD_CEIVA_STATICMAP + nr_parts = clps_static_partitions(&parsed_parts); + if (nr_parts > 0) { +--- linux-2.4.21/drivers/mtd/maps/cfi_flagadm.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/cfi_flagadm.c +@@ -1,7 +1,7 @@ + /* + * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -55,83 +56,33 @@ + #define FLASH_PARTITION3_ADDR 0x00240000 + #define FLASH_PARTITION3_SIZE 0x001C0000 + +-__u8 flagadm_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 flagadm_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 flagadm_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info flagadm_map = { +- name: "FlagaDM flash device", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: flagadm_read8, +- read16: flagadm_read16, +- read32: flagadm_read32, +- copy_from: flagadm_copy_from, +- write8: flagadm_write8, +- write16: flagadm_write16, +- write32: flagadm_write32, +- copy_to: flagadm_copy_to ++ .name = "FlagaDM flash device", ++ .size = FLASH_SIZE, ++ .bankwidth = 2, + }; + + struct mtd_partition flagadm_parts[] = { + { +- name : "Bootloader", +- offset : FLASH_PARTITION0_ADDR, +- size : FLASH_PARTITION0_SIZE ++ .name = "Bootloader", ++ .offset = FLASH_PARTITION0_ADDR, ++ .size = FLASH_PARTITION0_SIZE + }, + { +- name : "Kernel image", +- offset : FLASH_PARTITION1_ADDR, +- size : FLASH_PARTITION1_SIZE ++ .name = "Kernel image", ++ .offset = FLASH_PARTITION1_ADDR, ++ .size = FLASH_PARTITION1_SIZE + }, + { +- name : "Initial ramdisk image", +- offset : FLASH_PARTITION2_ADDR, +- size : FLASH_PARTITION2_SIZE ++ .name = "Initial ramdisk image", ++ .offset = FLASH_PARTITION2_ADDR, ++ .size = FLASH_PARTITION2_SIZE + }, + { +- name : "Persistant storage", +- offset : FLASH_PARTITION3_ADDR, +- size : FLASH_PARTITION3_SIZE ++ .name = "Persistant storage", ++ .offset = FLASH_PARTITION3_ADDR, ++ .size = FLASH_PARTITION3_SIZE + } + }; + +@@ -144,22 +95,26 @@ + printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + +- flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR, ++ flagadm_map.phys = FLASH_PHYS_ADDR; ++ flagadm_map.virt = ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + +- if (!flagadm_map.map_priv_1) { ++ if (!flagadm_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ ++ simple_map_init(&flagadm_map); ++ + mymtd = do_map_probe("cfi_probe", &flagadm_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT); + printk(KERN_NOTICE "FlagaDM flash device initialized\n"); + return 0; + } + +- iounmap((void *)flagadm_map.map_priv_1); ++ iounmap((void *)flagadm_map.virt); + return -ENXIO; + } + +@@ -169,9 +124,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (flagadm_map.map_priv_1) { +- iounmap((void *)flagadm_map.map_priv_1); +- flagadm_map.map_priv_1 = 0; ++ if (flagadm_map.virt) { ++ iounmap((void *)flagadm_map.virt); ++ flagadm_map.virt = 0; + } + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/chestnut.c +@@ -0,0 +1,91 @@ ++/* ++ * drivers/mtd/maps/chestnut.c ++ * ++ * $Id$ ++ * ++ * Flash map driver for IBM Chestnut (750FXGX Eval) ++ * ++ * Chose not to enable 8 bit flash as it contains the firmware and board ++ * info. Thus only the 32bit flash is supported. ++ * ++ * Author: ++ * ++ * 2004 (c) MontaVista Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct map_info chestnut32_map = { ++ .name = "User FS", ++ .size = CHESTNUT_32BIT_SIZE, ++ .bankwidth = 4, ++ .phys = CHESTNUT_32BIT_BASE, ++}; ++ ++static struct mtd_partition chestnut32_partitions[] = { ++ { ++ .name = "User FS", ++ .offset = 0, ++ .size = CHESTNUT_32BIT_SIZE, ++ } ++}; ++ ++static struct mtd_info *flash32; ++ ++int __init init_chestnut(void) ++{ ++ /* 32-bit FLASH */ ++ ++ chestnut32_map.virt = ioremap(chestnut32_map.phys, chestnut32_map.size); ++ ++ if (!chestnut32_map.virt) { ++ printk(KERN_NOTICE "Failed to ioremap 32-bit flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&chestnut32_map); ++ ++ flash32 = do_map_probe("cfi_probe", &chestnut32_map); ++ if (flash32) { ++ flash32->owner = THIS_MODULE; ++ add_mtd_partitions(flash32, chestnut32_partitions, ++ ARRAY_SIZE(chestnut32_partitions)); ++ } else { ++ printk(KERN_NOTICE "map probe failed for 32-bit flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit ++cleanup_chestnut(void) ++{ ++ if (flash32) { ++ del_mtd_partitions(flash32); ++ map_destroy(flash32); ++ } ++ ++ if (chestnut32_map.virt) { ++ iounmap((void *)chestnut32_map.virt); ++ chestnut32_map.virt = 0; ++ } ++} ++ ++module_init(init_chestnut); ++module_exit(cleanup_chestnut); ++ ++MODULE_DESCRIPTION("MTD map and partitions for IBM Chestnut (750fxgx Eval)"); ++MODULE_AUTHOR(""); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/maps/cstm_mips_ixx.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/cstm_mips_ixx.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. +@@ -33,55 +33,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +- +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #include +-#endif +- +-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #define CC_GCR 0xB4013818 +@@ -97,10 +55,17 @@ + #define CC_GPAICR 0xB4013804 + #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + ++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) + { ++ static DEFINE_SPINLOCK(vpp_lock); ++ static int vpp_count = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpp_lock, flags); ++ + if (vpp) { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ if (!vpp_count++) { + __u16 data; + __u8 data1; + static u8 first = 1; +@@ -116,10 +81,9 @@ + enabling vpp after powerup */ + udelay(40); + } +-#endif /* CONFIG_MIPS_ITE8172 */ + } +- else { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ } else { ++ if (!--vpp_count) { + __u16 data; + + // Set GPIO port B pin3 to high +@@ -127,26 +91,11 @@ + data = (data & 0xff3f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; +-#endif /* CONFIG_MIPS_ITE8172 */ + } ++ } ++ spin_unlock_irqrestore(&vpp_lock, flags); + } +- +-const struct map_info basic_cstm_mips_ixx_map = { +- NULL, +- 0, +- 0, +- cstm_mips_ixx_read8, +- cstm_mips_ixx_read16, +- cstm_mips_ixx_read32, +- cstm_mips_ixx_copy_from, +- cstm_mips_ixx_write8, +- cstm_mips_ixx_write16, +- cstm_mips_ixx_write32, +- cstm_mips_ixx_copy_to, +- cstm_mips_ixx_set_vpp, +- 0, +- 0 +-}; ++#endif + + /* board and partition description */ + +@@ -155,7 +104,7 @@ + char *name; + unsigned long window_addr; + unsigned long window_size; +- int buswidth; ++ int bankwidth; + int num_partitions; + }; + +@@ -167,7 +116,7 @@ + "big flash", // name + 0x08000000, // window_addr + 0x02000000, // window_size +- 4, // buswidth ++ 4, // bankwidth + 1, // num_partitions + } + +@@ -175,9 +124,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { // 28F128J3A in 2x16 configuration + { +- name: "main partition ", +- size: 0x02000000, // 128 x 2 x 128k byte sectors +- offset: 0, ++ .name = "main partition ", ++ .size = 0x02000000, // 128 x 2 x 128k byte sectors ++ .offset = 0, + }, + }, + }; +@@ -189,7 +138,7 @@ + "MTD flash", // name + CONFIG_MTD_CSTM_MIPS_IXX_START, // window_addr + CONFIG_MTD_CSTM_MIPS_IXX_LEN, // window_size +- CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // buswidth ++ CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // bankwidth + 1, // num_partitions + }, + +@@ -197,9 +146,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { + { +- name: "main partition", +- size: CONFIG_MTD_CSTM_MIPS_IXX_LEN, +- offset: 0, ++ .name = "main partition", ++ .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, ++ .offset = 0, + }, + }, + }; +@@ -216,17 +165,24 @@ + + /* Initialize mapping */ + for (i=0;imodule = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; + add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); +@@ -266,9 +222,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (cstm_mips_ixx_map[i].map_priv_1) { +- iounmap((void *)cstm_mips_ixx_map[i].map_priv_1); +- cstm_mips_ixx_map[i].map_priv_1 = 0; ++ if (cstm_mips_ixx_map[i].virt) { ++ iounmap((void *)cstm_mips_ixx_map[i].virt); ++ cstm_mips_ixx_map[i].virt = 0; + } + } + } +--- linux-2.4.21/drivers/mtd/maps/dbox2-flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/dbox2-flash.c +@@ -1,37 +1,61 @@ + /* +- * $Id$ ++ * $Id$ + * +- * Nokia / Sagem D-Box 2 flash driver ++ * D-Box 2 flash driver + */ + + #include + #include + #include ++#include + #include + #include + #include + #include + #include ++#include + + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +-static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw +- size: 128 * 1024, +- offset: 0, +- mask_flags: MTD_WRITEABLE}, +- {name: "PPC bootloader", // flfs +- size: 128 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "Kernel", // idxfs +- size: 768 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "System", // jffs +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}}; ++static struct mtd_partition partition_info[]= { ++ { ++ .name = "BR bootloader", ++ .size = 128 * 1024, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ }, ++ { ++ .name = "FLFS (U-Boot)", ++ .size = 128 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "Root (SquashFS)", ++ .size = 7040 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "var (JFFS2)", ++ .size = 896 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "Flash without bootloader", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 128 * 1024, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "Complete Flash", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ } ++}; + + #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) + +@@ -40,84 +64,36 @@ + + static struct mtd_info *mymtd; + +-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info dbox2_flash_map = { +- name: "D-Box 2 flash memory", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: dbox2_flash_read8, +- read16: dbox2_flash_read16, +- read32: dbox2_flash_read32, +- copy_from: dbox2_flash_copy_from, +- write8: dbox2_flash_write8, +- write16: dbox2_flash_write16, +- write32: dbox2_flash_write32, +- copy_to: dbox2_flash_copy_to ++ .name = "D-Box 2 flash memory", ++ .size = WINDOW_SIZE, ++ .bankwidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_dbox2_flash(void) + { + printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); +- dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!dbox2_flash_map.map_priv_1) { ++ if (!dbox2_flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&dbox2_flash_map); + + // Probe for dual Intel 28F320 or dual AMD + mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); + if (!mymtd) { + // Probe for single Intel 28F640 +- dbox2_flash_map.buswidth = 2; ++ dbox2_flash_map.bankwidth = 2; + + mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); + } + + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); +@@ -125,7 +101,7 @@ + return 0; + } + +- iounmap((void *)dbox2_flash_map.map_priv_1); ++ iounmap((void *)dbox2_flash_map.virt); + return -ENXIO; + } + +@@ -135,9 +111,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dbox2_flash_map.map_priv_1) { +- iounmap((void *)dbox2_flash_map.map_priv_1); +- dbox2_flash_map.map_priv_1 = 0; ++ if (dbox2_flash_map.virt) { ++ iounmap((void *)dbox2_flash_map.virt); ++ dbox2_flash_map.virt = 0; + } + } + +@@ -146,5 +122,5 @@ + + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Kári Davíðsson "); +-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); ++MODULE_AUTHOR("Kári Davíðsson , Bastian Blank , Alexander Wild "); ++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); +--- linux-2.4.21/drivers/mtd/maps/dc21285.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/dc21285.c +@@ -5,12 +5,14 @@ + * + * This code is GPL + * +- * $Id$ ++ * $Id$ + */ + #include + #include + #include + #include ++#include ++#include + + #include + #include +@@ -18,143 +20,199 @@ + + #include + #include ++#include + + +-static struct mtd_info *mymtd; ++static struct mtd_info *dc21285_mtd; + +-__u8 dc21285_read8(struct map_info *map, unsigned long ofs) ++#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) + { +- return *(__u8*)(map->map_priv_1 + ofs); ++ extern spinlock_t gpio_lock; ++ 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(&gpio_lock, flags); ++ cpld_modify(1, 1); ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ /* ++ * let the ISA bus to catch on... ++ */ ++ udelay(25); + } ++#else ++#define nw_en_write() do { } while (0) ++#endif + +-__u16 dc21285_read16(struct map_info *map, unsigned long ofs) ++static map_word dc21285_read8(struct map_info *map, unsigned long ofs) + { +- return *(__u16*)(map->map_priv_1 + ofs); ++ map_word val; ++ val.x[0] = *(uint8_t*)(map->virt + ofs); ++ return val; + } + +-__u32 dc21285_read32(struct map_info *map, unsigned long ofs) ++static map_word dc21285_read16(struct map_info *map, unsigned long ofs) + { +- return *(__u32*)(map->map_priv_1 + ofs); ++ map_word val; ++ val.x[0] = *(uint16_t*)(map->virt + ofs); ++ return val; + } + +-void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++static map_word dc21285_read32(struct map_info *map, unsigned long ofs) + { +- memcpy(to, (void*)(map->map_priv_1 + from), len); ++ map_word val; ++ val.x[0] = *(uint32_t*)(map->virt + ofs); ++ return val; + } + +-void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) ++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; +- *(__u8*)(map->map_priv_1 + adr) = d; ++ *(uint8_t*)(map->virt + adr) = d.x[0]; + } + +-void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) ++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; +- *(__u16*)(map->map_priv_1 + adr) = d; ++ *(uint16_t*)(map->virt + adr) = d.x[0]; + } + +-void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) ++static void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr) + { +- *(__u32*)(map->map_priv_1 + adr) = d; ++ if (machine_is_netwinder()) ++ nw_en_write(); ++ *(uint32_t*)(map->virt + adr) = d.x[0]; + } + +-void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len) + { +- switch (map->buswidth) { +- case 4: + while (len > 0) { +- __u32 d = *((__u32*)from)++; ++ map_word d; ++ d.x[0] = *((uint32_t*)from)++; + dc21285_write32(map, d, to); + to += 4; + len -= 4; + } +- break; +- case 2: ++} ++ ++static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ + while (len > 0) { +- __u16 d = *((__u16*)from)++; ++ map_word d; ++ d.x[0] = *((uint16_t*)from)++; + dc21285_write16(map, d, to); + to += 2; + len -= 2; + } +- break; +- case 1: +- while (len > 0) { +- __u8 d = *((__u8*)from)++; ++} ++ ++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); + to++; + len--; +- } +- break; +- } + } + +-struct map_info dc21285_map = { +- name: "DC21285 flash", +- size: 16*1024*1024, +- read8: dc21285_read8, +- read16: dc21285_read16, +- read32: dc21285_read32, +- copy_from: dc21285_copy_from, +- write8: dc21285_write8, +- write16: dc21285_write16, +- write32: dc21285_write32, +- copy_to: dc21285_copy_to ++static struct map_info dc21285_map = { ++ .name = "DC21285 flash", ++ .phys = NO_XIP, ++ .size = 16*1024*1024, ++ .copy_from = dc21285_copy_from, + }; + + + /* Partition stuff */ ++#ifdef CONFIG_MTD_PARTITIONS + static struct mtd_partition *dc21285_parts; ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++#endif + +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); +- +-int __init init_dc21285(void) ++static int __init init_dc21285(void) + { +- /* Determine buswidth */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ int nrparts; ++#endif ++ ++ /* Determine bankwidth */ + switch (*CSR_SA110_CNTL & (3<<14)) { + case SA110_CNTL_ROMWIDTH_8: +- dc21285_map.buswidth = 1; ++ 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.buswidth = 2; ++ 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.buswidth = 4; ++ 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 buswidth\n"); ++ printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); + return -ENXIO; + } +- printk (KERN_NOTICE "DC21285 flash support (%d-bit buswidth)\n", +- dc21285_map.buswidth*8); ++ printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n", ++ dc21285_map.bankwidth*8); + + /* Let's map the flash area */ +- dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); +- if (!dc21285_map.map_priv_1) { ++ dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024); ++ if (!dc21285_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + +- mymtd = do_map_probe("cfi_probe", &dc21285_map); +- if (mymtd) { +- int nrparts = 0; ++ if (machine_is_ebsa285()) { ++ dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); ++ } else { ++ dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); ++ } + +- mymtd->module = THIS_MODULE; ++ if (!dc21285_mtd) { ++ iounmap(dc21285_map.virt); ++ return -ENXIO; ++ } + +- /* partition fixup */ ++ dc21285_mtd->owner = THIS_MODULE; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); ++#ifdef CONFIG_MTD_PARTITIONS ++ nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0); ++ if (nrparts > 0) ++ add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts); ++ else + #endif +- if (nrparts > 0) { +- add_mtd_partitions(mymtd, dc21285_parts, nrparts); +- } else if (nrparts == 0) { +- printk(KERN_NOTICE "RedBoot partition table failed\n"); +- add_mtd_device(mymtd); +- } ++ add_mtd_device(dc21285_mtd); + ++ 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 +@@ -167,27 +225,23 @@ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); + /* tristate time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); +- +- return 0; + } + +- iounmap((void *)dc21285_map.map_priv_1); +- return -ENXIO; ++ return 0; + } + + static void __exit cleanup_dc21285(void) + { +- if (mymtd) { +- del_mtd_device(mymtd); +- map_destroy(mymtd); +- mymtd = NULL; +- } +- if (dc21285_map.map_priv_1) { +- iounmap((void *)dc21285_map.map_priv_1); +- dc21285_map.map_priv_1 = 0; +- } +- if(dc21285_parts) ++#ifdef CONFIG_MTD_PARTITIONS ++ if (dc21285_parts) { ++ del_mtd_partitions(dc21285_mtd); + kfree(dc21285_parts); ++ } else ++#endif ++ del_mtd_device(dc21285_mtd); ++ ++ map_destroy(dc21285_mtd); ++ iounmap(dc21285_map.virt); + } + + module_init(init_dc21285); +--- linux-2.4.21/drivers/mtd/maps/dilnetpc.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/dilnetpc.c +@@ -14,7 +14,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id$ ++ * $Id$ + * + * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems + * featuring the AMD Elan SC410 processor. There are two variants of this +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,7 +37,7 @@ + #include + + /* +-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks. ++** The DIL/NetPC keeps its BIOS in two distinct flash blocks. + ** Destroying any of these blocks transforms the DNPC into + ** a paperweight (albeit not a very useful one, considering + ** it only weighs a few grams). +@@ -189,45 +190,6 @@ + } + + +-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + /* + ************************************************************ +@@ -235,7 +197,7 @@ + ************************************************************ + */ + +-static spinlock_t dnpc_spin = SPIN_LOCK_UNLOCKED; ++static DEFINE_SPINLOCK(dnpc_spin); + static int vpp_counter = 0; + /* + ** This is what has to be done for the DNP board .. +@@ -288,19 +250,11 @@ + #define WINDOW_ADDR FLASH_BASE + + static struct map_info dnpc_map = { +- name: "ADNP Flash Bank", +- size: ADNP_WINDOW_SIZE, +- buswidth: 1, +- read8: dnpc_read8, +- read16: dnpc_read16, +- read32: dnpc_read32, +- copy_from: dnpc_copy_from, +- write8: dnpc_write8, +- write16: dnpc_write16, +- write32: dnpc_write32, +- copy_to: dnpc_copy_to, +- set_vpp: adnp_set_vpp, +- map_priv_2: WINDOW_ADDR ++ .name = "ADNP Flash Bank", ++ .size = ADNP_WINDOW_SIZE, ++ .bankwidth = 1, ++ .set_vpp = adnp_set_vpp, ++ .phys = WINDOW_ADDR + }; + + /* +@@ -316,29 +270,29 @@ + static struct mtd_partition partition_info[]= + { + { +- name: "ADNP boot", +- offset: 0, +- size: 0xf0000, ++ .name = "ADNP boot", ++ .offset = 0, ++ .size = 0xf0000, + }, + { +- name: "ADNP system BIOS", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x10000, ++ .name = "ADNP system BIOS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x10000, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + { +- name: "ADNP file system", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x2f0000, ++ .name = "ADNP file system", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x2f0000, + }, + { +- name: "ADNP system BIOS entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -369,21 +323,21 @@ + static struct mtd_partition higlvl_partition_info[]= + { + { +- name: "ADNP boot block", +- offset: 0, +- size: CONFIG_MTD_DILNETPC_BOOTSIZE, ++ .name = "ADNP boot block", ++ .offset = 0, ++ .size = CONFIG_MTD_DILNETPC_BOOTSIZE, + }, + { +- name: "ADNP file system space", +- offset: MTDPART_OFS_NXTBLK, +- size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, ++ .name = "ADNP file system space", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, + }, + { +- name: "ADNP system BIOS + BIOS Entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS + BIOS Entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -447,18 +401,19 @@ + } + + printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", +- is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2); ++ is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); + +- dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map.virt = ioremap_nocache(dnpc_map.phys, dnpc_map.size); + +- dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map_flash(dnpc_map.phys, dnpc_map.size); + +- if (!dnpc_map.map_priv_1) { ++ if (!dnpc_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ simple_map_init(&dnpc_map); + +- printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1); ++ printk("FLASH virtual address: 0x%p\n", dnpc_map.virt); + + mymtd = do_map_probe("jedec_probe", &dnpc_map); + +@@ -475,11 +430,11 @@ + mymtd->erasesize = 0x10000; + + if (!mymtd) { +- iounmap((void *)dnpc_map.map_priv_1); ++ iounmap(dnpc_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* + ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions() +@@ -525,10 +480,10 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dnpc_map.map_priv_1) { +- iounmap((void *)dnpc_map.map_priv_1); ++ if (dnpc_map.virt) { ++ iounmap(dnpc_map.virt); + dnpc_unmap_flash(); +- dnpc_map.map_priv_1 = 0; ++ dnpc_map.virt = NULL; + } + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/dmv182.c +@@ -0,0 +1,149 @@ ++ ++/* ++ * drivers/mtd/maps/svme182.c ++ * ++ * Flash map driver for the Dy4 SVME182 board ++ * ++ * $Id$ ++ * ++ * Copyright 2003-2004, TimeSys Corporation ++ * ++ * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * This driver currently handles only the 16MiB user flash bank 1 on the ++ * board. It does not provide access to bank 0 (contains the Dy4 FFW), bank 2 ++ * (VxWorks boot), or the optional 48MiB expansion flash. ++ * ++ * scott.wood@timesys.com: On the newer boards with 128MiB flash, it ++ * now supports the first 96MiB (the boot flash bank containing FFW ++ * is excluded). The VxWorks loader is in partition 1. ++ */ ++ ++#define FLASH_BASE_ADDR 0xf0000000 ++#define FLASH_BANK_SIZE (128*1024*1024) ++ ++MODULE_AUTHOR("Scott Wood, TimeSys Corporation "); ++MODULE_DESCRIPTION("User-programmable flash device on the Dy4 SVME182 board"); ++MODULE_LICENSE("GPL"); ++ ++static struct map_info svme182_map = { ++ .name = "Dy4 SVME182", ++ .bankwidth = 32, ++ .size = 128 * 1024 * 1024 ++}; ++ ++#define BOOTIMAGE_PART_SIZE ((6*1024*1024)-RESERVED_PART_SIZE) ++ ++// Allow 6MiB for the kernel ++#define NEW_BOOTIMAGE_PART_SIZE (6 * 1024 * 1024) ++// Allow 1MiB for the bootloader ++#define NEW_BOOTLOADER_PART_SIZE (1024 * 1024) ++// Use the remaining 9MiB at the end of flash for the RFS ++#define NEW_RFS_PART_SIZE (0x01000000 - NEW_BOOTLOADER_PART_SIZE - \ ++ NEW_BOOTIMAGE_PART_SIZE) ++ ++static struct mtd_partition svme182_partitions[] = { ++ // The Lower PABS is only 128KiB, but the partition code doesn't ++ // like partitions that don't end on the largest erase block ++ // size of the device, even if all of the erase blocks in the ++ // partition are small ones. The hardware should prevent ++ // writes to the actual PABS areas. ++ { ++ name: "Lower PABS and CPU 0 bootloader or kernel", ++ size: 6*1024*1024, ++ offset: 0, ++ }, ++ { ++ name: "Root Filesystem", ++ size: 10*1024*1024, ++ offset: MTDPART_OFS_NXTBLK ++ }, ++ { ++ name: "CPU1 Bootloader", ++ size: 1024*1024, ++ offset: MTDPART_OFS_NXTBLK, ++ }, ++ { ++ name: "Extra", ++ size: 110*1024*1024, ++ offset: MTDPART_OFS_NXTBLK ++ }, ++ { ++ name: "Foundation Firmware and Upper PABS", ++ size: 1024*1024, ++ offset: MTDPART_OFS_NXTBLK, ++ mask_flags: MTD_WRITEABLE // read-only ++ } ++}; ++ ++static struct mtd_info *this_mtd; ++ ++static int __init init_svme182(void) ++{ ++ struct mtd_partition *partitions; ++ int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition); ++ ++ partitions = svme182_partitions; ++ ++ svme182_map.virt = ioremap(FLASH_BASE_ADDR, svme182_map.size); ++ ++ if (svme182_map.virt == 0) { ++ printk("Failed to ioremap FLASH memory area.\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&svme182_map); ++ ++ this_mtd = do_map_probe("cfi_probe", &svme182_map); ++ if (!this_mtd) ++ { ++ iounmap((void *)svme182_map.virt); ++ return -ENXIO; ++ } ++ ++ printk(KERN_NOTICE "SVME182 flash device: %dMiB at 0x%08x\n", ++ this_mtd->size >> 20, FLASH_BASE_ADDR); ++ ++ this_mtd->owner = THIS_MODULE; ++ add_mtd_partitions(this_mtd, partitions, num_parts); ++ ++ return 0; ++} ++ ++static void __exit cleanup_svme182(void) ++{ ++ if (this_mtd) ++ { ++ del_mtd_partitions(this_mtd); ++ map_destroy(this_mtd); ++ } ++ ++ if (svme182_map.virt) ++ { ++ iounmap((void *)svme182_map.virt); ++ svme182_map.virt = 0; ++ } ++ ++ return; ++} ++ ++module_init(init_svme182); ++module_exit(cleanup_svme182); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ebony.c +@@ -0,0 +1,163 @@ ++/* ++ * $Id$ ++ * ++ * Mapping for Ebony user flash ++ * ++ * Matt Porter ++ * ++ * Copyright 2002-2004 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *flash; ++ ++static struct map_info ebony_small_map = { ++ .name = "Ebony small flash", ++ .size = EBONY_SMALL_FLASH_SIZE, ++ .bankwidth = 1, ++}; ++ ++static struct map_info ebony_large_map = { ++ .name = "Ebony large flash", ++ .size = EBONY_LARGE_FLASH_SIZE, ++ .bankwidth = 1, ++}; ++ ++static struct mtd_partition ebony_small_partitions[] = { ++ { ++ .name = "OpenBIOS", ++ .offset = 0x0, ++ .size = 0x80000, ++ } ++}; ++ ++static struct mtd_partition ebony_large_partitions[] = { ++ { ++ .name = "fs", ++ .offset = 0, ++ .size = 0x380000, ++ }, ++ { ++ .name = "firmware", ++ .offset = 0x380000, ++ .size = 0x80000, ++ } ++}; ++ ++int __init init_ebony(void) ++{ ++ u8 fpga0_reg; ++ u8 __iomem *fpga0_adr; ++ unsigned long long small_flash_base, large_flash_base; ++ ++ fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); ++ if (!fpga0_adr) ++ return -ENOMEM; ++ ++ fpga0_reg = readb(fpga0_adr); ++ iounmap(fpga0_adr); ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH2; ++ else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH1; ++ else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_LOW2; ++ else ++ small_flash_base = EBONY_SMALL_FLASH_LOW1; ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_ONBRD_FLASH_EN(fpga0_reg)) ++ large_flash_base = EBONY_LARGE_FLASH_LOW; ++ else ++ large_flash_base = EBONY_LARGE_FLASH_HIGH; ++ ++ ebony_small_map.phys = small_flash_base; ++ ebony_small_map.virt = ioremap64(small_flash_base, ++ ebony_small_map.size); ++ ++ if (!ebony_small_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_small_map); ++ ++ flash = do_map_probe("jedec_probe", &ebony_small_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_small_partitions, ++ ARRAY_SIZE(ebony_small_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ ebony_large_map.phys = large_flash_base; ++ ebony_large_map.virt = ioremap64(large_flash_base, ++ ebony_large_map.size); ++ ++ if (!ebony_large_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_large_map); ++ ++ flash = do_map_probe("jedec_probe", &ebony_large_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_large_partitions, ++ ARRAY_SIZE(ebony_large_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit cleanup_ebony(void) ++{ ++ if (flash) { ++ del_mtd_partitions(flash); ++ map_destroy(flash); ++ } ++ ++ if (ebony_small_map.virt) { ++ iounmap(ebony_small_map.virt); ++ ebony_small_map.virt = NULL; ++ } ++ ++ if (ebony_large_map.virt) { ++ iounmap(ebony_large_map.virt); ++ ebony_large_map.virt = NULL; ++ } ++} ++ ++module_init(init_ebony); ++module_exit(cleanup_ebony); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matt Porter "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); +--- linux-2.4.21/drivers/mtd/maps/edb7312.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/edb7312.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Handle mapping of the NOR flash on Cogent EDB7312 boards + * +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,69 +28,19 @@ + #define BUSWIDTH 2 + #define FLASH_BLOCKSIZE_MAIN 0x20000 + #define FLASH_NUMBLOCKS_MAIN 128 +-/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +-#define PROBETYPES { "cfi_probe", 0 } ++/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */ ++#define PROBETYPES { "cfi_probe", NULL } + + #define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ + #define MTDID "edb7312-nor" /* for mtdparts= partitioning */ + + static struct mtd_info *mymtd; + +-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + struct map_info edb7312nor_map = { +- name: "NOR flash on EDB7312", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: edb7312nor_read8, +- read16: edb7312nor_read16, +- read32: edb7312nor_read32, +- copy_from: edb7312nor_copy_from, +- write8: edb7312nor_write8, +- write16: edb7312nor_write16, +- write32: edb7312nor_write32, +- copy_to: edb7312nor_copy_to ++ .name = "NOR flash on EDB7312", ++ .size = WINDOW_SIZE, ++ .bankwidth = BUSWIDTH, ++ .phys = WINDOW_ADDR, + }; + + #ifdef CONFIG_MTD_PARTITIONS +@@ -100,29 +51,23 @@ + static struct mtd_partition static_partitions[3] = + { + { +- name: "ARMboot", +- size: 0x40000, +- offset: 0 ++ .name = "ARMboot", ++ .size = 0x40000, ++ .offset = 0 + }, + { +- name: "Kernel", +- size: 0x200000, +- offset: 0x40000 ++ .name = "Kernel", ++ .size = 0x200000, ++ .offset = 0x40000 + }, + { +- name: "RootFS", +- size: 0xDC0000, +- offset: 0x240000 ++ .name = "RootFS", ++ .size = 0xDC0000, ++ .offset = 0x240000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); +-#endif ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + #endif + +@@ -137,32 +82,32 @@ + + printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", + WINDOW_SIZE, WINDOW_ADDR); +- edb7312nor_map.map_priv_1 = (unsigned long) +- ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ edb7312nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!edb7312nor_map.map_priv_1) { ++ if (!edb7312nor_map.virt) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + ++ simple_map_init(&edb7312nor_map); ++ + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &edb7312nor_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); + if (mtd_parts_nb > 0) +- part_type = "command line"; +-#endif ++ part_type = "detected"; ++ + if (mtd_parts_nb == 0) + { + mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ mtd_parts_nb = ARRAY_SIZE(static_partitions); + part_type = "static"; + } + #endif +@@ -178,7 +123,7 @@ + return 0; + } + +- iounmap((void *)edb7312nor_map.map_priv_1); ++ iounmap((void *)edb7312nor_map.virt); + return -ENXIO; + } + +@@ -188,9 +133,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (edb7312nor_map.map_priv_1) { +- iounmap((void *)edb7312nor_map.map_priv_1); +- edb7312nor_map.map_priv_1 = 0; ++ if (edb7312nor_map.virt) { ++ iounmap((void *)edb7312nor_map.virt); ++ edb7312nor_map.virt = 0; + } + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/epxa10db-flash.c +@@ -0,0 +1,176 @@ ++/* ++ * Flash memory access on EPXA based devices ++ * ++ * (C) 2000 Nicolas Pitre ++ * Copyright (C) 2001 Altera Corporation ++ * Copyright (C) 2001 Red Hat, Inc. ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#ifdef CONFIG_EPXA10DB ++#define BOARD_NAME "EPXA10DB" ++#else ++#define BOARD_NAME "EPXA1DB" ++#endif ++ ++static int nr_parts = 0; ++static struct mtd_partition *parts; ++ ++static struct mtd_info *mymtd; ++ ++static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++ ++ ++static struct map_info epxa_map = { ++ .name = "EPXA flash", ++ .size = FLASH_SIZE, ++ .bankwidth = 2, ++ .phys = FLASH_START, ++}; ++ ++static const char *probes[] = { "RedBoot", "afs", NULL }; ++ ++static int __init epxa_mtd_init(void) ++{ ++ int i; ++ ++ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); ++ ++ epxa_map.virt = ioremap(FLASH_START, FLASH_SIZE); ++ if (!epxa_map.virt) { ++ printk("Failed to ioremap %s flash\n",BOARD_NAME); ++ return -EIO; ++ } ++ simple_map_init(&epxa_map); ++ ++ mymtd = do_map_probe("cfi_probe", &epxa_map); ++ if (!mymtd) { ++ iounmap((void *)epxa_map.virt); ++ return -ENXIO; ++ } ++ ++ mymtd->owner = THIS_MODULE; ++ ++ /* Unlock the flash device. */ ++ if(mymtd->unlock){ ++ for (i=0; inumeraseregions;i++){ ++ int j; ++ for(j=0;jeraseregions[i].numblocks;j++){ ++ mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); ++ } ++ } ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0); ++ ++ if (nr_parts > 0) { ++ add_mtd_partitions(mymtd, parts, nr_parts); ++ return 0; ++ } ++#endif ++ /* No recognised partitioning schemes found - use defaults */ ++ nr_parts = epxa_default_partitions(mymtd, &parts); ++ if (nr_parts > 0) { ++ add_mtd_partitions(mymtd, parts, nr_parts); ++ return 0; ++ } ++ ++ /* If all else fails... */ ++ add_mtd_device(mymtd); ++ return 0; ++} ++ ++static void __exit epxa_mtd_cleanup(void) ++{ ++ if (mymtd) { ++ if (nr_parts) ++ del_mtd_partitions(mymtd); ++ else ++ del_mtd_device(mymtd); ++ map_destroy(mymtd); ++ } ++ if (epxa_map.virt) { ++ iounmap((void *)epxa_map.virt); ++ epxa_map.virt = 0; ++ } ++} ++ ++ ++/* ++ * This will do for now, once we decide which bootldr we're finally ++ * going to use then we'll remove this function and do it properly ++ * ++ * Partions are currently (as offsets from base of flash): ++ * 0x00000000 - 0x003FFFFF - bootloader (!) ++ * 0x00400000 - 0x00FFFFFF - Flashdisk ++ */ ++ ++static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) ++{ ++ struct mtd_partition *parts; ++ int ret, i; ++ int npartitions = 0; ++ char *names; ++ const char *name = "jffs"; ++ ++ printk("Using default partitions for %s\n",BOARD_NAME); ++ npartitions=1; ++ parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); ++ memzero(parts,npartitions*sizeof(*parts)+strlen(name)); ++ if (!parts) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ i=0; ++ names = (char *)&parts[npartitions]; ++ parts[i].name = names; ++ names += strlen(name) + 1; ++ strcpy(parts[i].name, name); ++ ++#ifdef CONFIG_EPXA10DB ++ parts[i].size = FLASH_SIZE-0x00400000; ++ parts[i].offset = 0x00400000; ++#else ++ parts[i].size = FLASH_SIZE-0x00180000; ++ parts[i].offset = 0x00180000; ++#endif ++ ++ out: ++ *pparts = parts; ++ return npartitions; ++} ++ ++ ++module_init(epxa_mtd_init); ++module_exit(epxa_mtd_cleanup); ++ ++MODULE_AUTHOR("Clive Davies"); ++MODULE_DESCRIPTION("Altera epxa mtd flash map"); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/maps/fortunet.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/fortunet.c +@@ -1,11 +1,12 @@ + /* fortunet.c memory map + * +- * $Id$ ++ * $Id$ + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -23,8 +24,8 @@ + + struct map_region + { +- int window_addr_phyical; +- int altbuswidth; ++ int window_addr_physical; ++ int altbankwidth; + struct map_info map_info; + struct mtd_info *mymtd; + struct mtd_partition parts[MAX_NUM_PARTITIONS]; +@@ -37,57 +38,10 @@ + static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; + + +-__u8 fortunet_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 fortunet_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 fortunet_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} + + struct map_info default_map = { +- size: DEF_WINDOW_SIZE, +- buswidth: 4, +- read8: fortunet_read8, +- read16: fortunet_read16, +- read32: fortunet_read32, +- copy_from: fortunet_copy_from, +- write8: fortunet_write8, +- write16: fortunet_write16, +- write32: fortunet_write32, +- copy_to: fortunet_copy_to ++ .size = DEF_WINDOW_SIZE, ++ .bankwidth = 4, + }; + + static char * __init get_string_option(char *dest,int dest_size,char *sor) +@@ -147,8 +101,8 @@ + get_options (get_string_option(string,sizeof(string),line),6,params); + if(params[0]<1) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " +- " name,region-number[,base,size,buswidth,altbuswidth]\n"); ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " ++ " name,region-number[,base,size,bankwidth,altbankwidth]\n"); + return 1; + } + if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) +@@ -161,14 +115,14 @@ + memcpy(&map_regions[params[1]].map_info, + &default_map,sizeof(map_regions[params[1]].map_info)); + map_regions_set[params[1]] = 1; +- map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY; +- map_regions[params[1]].altbuswidth = 2; ++ map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; ++ map_regions[params[1]].altbankwidth = 2; + map_regions[params[1]].mymtd = NULL; + map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; + strcpy(map_regions[params[1]].map_info.name,string); + if(params[0]>1) + { +- map_regions[params[1]].window_addr_phyical = params[2]; ++ map_regions[params[1]].window_addr_physical = params[2]; + } + if(params[0]>2) + { +@@ -176,23 +130,23 @@ + } + if(params[0]>3) + { +- map_regions[params[1]].map_info.buswidth = params[4]; ++ map_regions[params[1]].map_info.bankwidth = params[4]; + } + if(params[0]>4) + { +- map_regions[params[1]].altbuswidth = params[5]; ++ map_regions[params[1]].altbankwidth = params[5]; + } + return 1; + } + +-static int __init MTD_New_Partion(char *line) ++static int __init MTD_New_Partition(char *line) + { + char string[MAX_NAME_SIZE]; + int params[4]; + get_options (get_string_option(string,sizeof(string),line),4,params); + if(params[0]<3) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion " ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " + " name,region-number,size,offset\n"); + return 1; + } +@@ -204,7 +158,7 @@ + } + if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) + { +- printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); ++ printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); + return 1; + } + map_regions[params[1]].parts[map_regions_parts[params[1]]].name = +@@ -220,7 +174,10 @@ + } + + __setup("MTD_Region=", MTD_New_Region); +-__setup("MTD_Partion=", MTD_New_Partion); ++__setup("MTD_Partition=", MTD_New_Partition); ++ ++/* Backwards-spelling-compatibility */ ++__setup("MTD_Partion=", MTD_New_Partition); + + int __init init_fortunet(void) + { +@@ -229,14 +186,14 @@ + { + if(map_regions_parts[ix]&&(!map_regions_set[ix])) + { +- printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n", ++ printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", + ix); + memset(&map_regions[ix],0,sizeof(map_regions[ix])); + memcpy(&map_regions[ix].map_info,&default_map, + sizeof(map_regions[ix].map_info)); + map_regions_set[ix] = 1; +- map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY; +- map_regions[ix].altbuswidth = 2; ++ map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; ++ map_regions[ix].altbankwidth = 2; + map_regions[ix].mymtd = NULL; + map_regions[ix].map_info.name = map_regions[ix].map_name; + strcpy(map_regions[ix].map_info.name,"FORTUNET"); +@@ -244,38 +201,43 @@ + if(map_regions_set[ix]) + { + iy++; +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " + " address %x size %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].window_addr_phyical, ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- map_regions[ix].map_info.map_priv_1 = +- (int)ioremap_nocache( +- map_regions[ix].window_addr_phyical, ++ ++ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, ++ ++ map_regions[ix].map_info.virt = ++ ioremap_nocache( ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- if(!map_regions[ix].map_info.map_priv_1) ++ if(!map_regions[ix].map_info.virt) + { + printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", + map_regions[ix].map_info.name); + return -ENXIO; + } +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n", ++ simple_map_init(&map_regions[ix].map_info); ++ ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].map_info.map_priv_1); ++ map_regions[ix].map_info.virt); + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + if((!map_regions[ix].mymtd)&&( +- map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth)) ++ map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth)) + { +- printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth " + "for %s flash.\n", + map_regions[ix].map_info.name); +- map_regions[ix].map_info.buswidth = +- map_regions[ix].altbuswidth; ++ map_regions[ix].map_info.bankwidth = ++ map_regions[ix].altbankwidth; + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + } +- map_regions[ix].mymtd->module = THIS_MODULE; ++ map_regions[ix].mymtd->owner = THIS_MODULE; + add_mtd_partitions(map_regions[ix].mymtd, + map_regions[ix].parts,map_regions_parts[ix]); + } +@@ -297,7 +259,7 @@ + del_mtd_partitions( map_regions[ix].mymtd ); + map_destroy( map_regions[ix].mymtd ); + } +- iounmap((void *)map_regions[ix].map_info.map_priv_1); ++ iounmap((void *)map_regions[ix].map_info.virt); + } + } + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/h720x-flash.c +@@ -0,0 +1,144 @@ ++/* ++ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based ++ * evaluation boards ++ * ++ * $Id$ ++ * ++ * (C) 2002 Jungjun Kim ++ * 2003 Thomas Gleixner ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *mymtd; ++ ++static struct map_info h720x_map = { ++ .name = "H720X", ++ .bankwidth = 4, ++ .size = FLASH_SIZE, ++ .phys = FLASH_PHYS, ++}; ++ ++static struct mtd_partition h720x_partitions[] = { ++ { ++ .name = "ArMon", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Env", ++ .size = 0x00040000, ++ .offset = 0x00080000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Kernel", ++ .size = 0x00180000, ++ .offset = 0x000c0000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Ramdisk", ++ .size = 0x00400000, ++ .offset = 0x00240000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND ++ } ++}; ++ ++#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) ++ ++static int nr_mtd_parts; ++static struct mtd_partition *mtd_parts; ++static const char *probes[] = { "cmdlinepart", NULL }; ++ ++/* ++ * Initialize FLASH support ++ */ ++int __init h720x_mtd_init(void) ++{ ++ ++ char *part_type = NULL; ++ ++ h720x_map.virt = ioremap(FLASH_PHYS, FLASH_SIZE); ++ ++ if (!h720x_map.virt) { ++ printk(KERN_ERR "H720x-MTD: ioremap failed\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&h720x_map); ++ ++ // Probe for flash bankwidth 4 ++ printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ if (!mymtd) { ++ printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); ++ // Probe for bankwidth 2 ++ h720x_map.bankwidth = 2; ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ } ++ ++ if (mymtd) { ++ mymtd->owner = THIS_MODULE; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); ++ if (nr_mtd_parts > 0) ++ part_type = "command line"; ++#endif ++ if (nr_mtd_parts <= 0) { ++ mtd_parts = h720x_partitions; ++ nr_mtd_parts = NUM_PARTITIONS; ++ part_type = "builtin"; ++ } ++ printk(KERN_INFO "Using %s partition table\n", part_type); ++ add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); ++ return 0; ++ } ++ ++ iounmap((void *)h720x_map.virt); ++ return -ENXIO; ++} ++ ++/* ++ * Cleanup ++ */ ++static void __exit h720x_mtd_cleanup(void) ++{ ++ ++ if (mymtd) { ++ del_mtd_partitions(mymtd); ++ map_destroy(mymtd); ++ } ++ ++ /* Free partition info, if commandline partition was used */ ++ if (mtd_parts && (mtd_parts != h720x_partitions)) ++ kfree (mtd_parts); ++ ++ if (h720x_map.virt) { ++ iounmap((void *)h720x_map.virt); ++ h720x_map.virt = 0; ++ } ++} ++ ++ ++module_init(h720x_mtd_init); ++module_exit(h720x_mtd_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Thomas Gleixner "); ++MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ichxrom.c +@@ -0,0 +1,383 @@ ++/* ++ * ichxrom.c ++ * ++ * Normal mappings of chips in physical memory ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define 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); ++ ++ /* Free all of the mtd devices */ ++ list_for_each_entry_safe(map, scratch, &window->maps, list) { ++ if (map->rsrc.parent) ++ release_resource(&map->rsrc); ++ del_mtd_device(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 __devinit 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" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ window->rsrc.start, window->rsrc.end); ++ } ++ ++ /* 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, 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(%u) larger than window(%lu). fixing...\n", ++ 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 (add_mtd_device(map->mtd)) { ++ 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 */ ++ if (map) { ++ kfree(map); ++ } ++ /* See if I have any map structures */ ++ if (list_empty(&window->maps)) { ++ ichxrom_cleanup(window); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++ ++static void __devexit ichxrom_remove_one (struct pci_dev *pdev) ++{ ++ struct ichxrom_window *window = &ichxrom_window; ++ ichxrom_cleanup(window); ++} ++ ++static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { 0, }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl); ++ ++#if 0 ++static struct pci_driver ichxrom_driver = { ++ .name = MOD_NAME, ++ .id_table = ichxrom_pci_tbl, ++ .probe = ichxrom_init_one, ++ .remove = ichxrom_remove_one, ++}; ++#endif ++ ++static 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_find_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_module_init(&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 "); ++MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge"); +--- linux-2.4.21/drivers/mtd/maps/impa7.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/impa7.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Handle mapping of the NOR flash on implementa A7 boards + * +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -29,83 +30,25 @@ + #define NUM_FLASHBANKS 2 + #define BUSWIDTH 4 + +-/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +-#define PROBETYPES { "jedec_probe", 0 } ++/* can be { "cfi_probe", "jedec_probe", "map_rom", NULL } */ ++#define PROBETYPES { "jedec_probe", NULL } + + #define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ + #define MTDID "impa7-%d" /* for mtdparts= partitioning */ + +-static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; +- +-__u8 impa7_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 impa7_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 impa7_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++static struct mtd_info *impa7_mtd[NUM_FLASHBANKS]; + +-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info impa7_map[NUM_FLASHBANKS] = { + { +- name: "impA7 NOR Flash Bank #0", +- size: WINDOW_SIZE0, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .bankwidth = BUSWIDTH, + }, + { +- name: "impA7 NOR Flash Bank #1", +- size: WINDOW_SIZE1, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .bankwidth = BUSWIDTH, + }, + }; + +@@ -117,24 +60,18 @@ + static struct mtd_partition static_partitions[] = + { + { +- name: "FileSystem", +- size: 0x800000, +- offset: 0x00000000 ++ .name = "FileSystem", ++ .size = 0x800000, ++ .offset = 0x00000000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); +-#endif ++static int mtd_parts_nb[NUM_FLASHBANKS]; ++static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; + + #endif + +-static int mtd_parts_nb = 0; +-static struct mtd_partition *mtd_parts = 0; ++static const char *probes[] = { "cmdlinepart", NULL }; + + int __init init_impa7(void) + { +@@ -146,20 +83,20 @@ + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, + }; +- char mtdid[10]; + int devicesfound = 0; + + for(i=0; imodule = THIS_MODULE; +- add_mtd_device(impa7_mtd[i]); ++ if (impa7_mtd[i]) { ++ impa7_mtd[i]->owner = THIS_MODULE; + devicesfound++; + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- sprintf(mtdid, MTDID, i); +- mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], +- &mtd_parts, +- mtdid); +- if (mtd_parts_nb > 0) ++ mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], ++ probes, ++ &mtd_parts[i], ++ 0); ++ if (mtd_parts_nb[i] > 0) { + part_type = "command line"; +-#endif +- if (mtd_parts_nb <= 0) +- { +- mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ } else { ++ mtd_parts[i] = static_partitions; ++ mtd_parts_nb[i] = ARRAY_SIZE(static_partitions); + part_type = "static"; + } +- if (mtd_parts_nb <= 0) +- { +- printk(KERN_NOTICE MSG_PREFIX +- "no partition info available\n"); +- } +- else +- { ++ + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", + part_type); + add_mtd_partitions(impa7_mtd[i], +- mtd_parts, mtd_parts_nb); +- } ++ mtd_parts[i], mtd_parts_nb[i]); ++#else ++ add_mtd_device(impa7_mtd[i]); ++ + #endif + } + else +- iounmap((void *)impa7_map[i].map_priv_1); ++ iounmap((void *)impa7_map[i].virt); + } + return devicesfound == 0 ? -ENXIO : 0; + } +@@ -211,17 +139,16 @@ + static void __exit cleanup_impa7(void) + { + int i; +- for (i=0; i ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++// board specific stuff - sorry, it should be in arch/arm/mach-*. ++#ifdef CONFIG_ARCH_INTEGRATOR ++ ++#define FLASH_BASE INTEGRATOR_FLASH_BASE ++#define FLASH_SIZE INTEGRATOR_FLASH_SIZE ++ ++#define FLASH_PART_SIZE 0x400000 ++ ++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) ++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) ++#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) ++#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) ++ ++/* ++ * Initialise the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Set write enable bit in EBI reg ++ */ ++static void armflash_flash_init(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++/* ++ * Shutdown the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Clear write enable bit in EBI reg ++ */ ++static void armflash_flash_exit(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ /* ++ * Clear the write enable bit in system controller EBI register. ++ */ ++ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++static void armflash_flash_wp(int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLC; ++ else ++ reg = SC_CTRLS; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLS; ++ else ++ reg = SC_CTRLC; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); ++} ++#endif ++ ++#ifdef CONFIG_ARCH_P720T ++ ++#define FLASH_BASE (0x04000000) ++#define FLASH_SIZE (64*1024*1024) ++ ++#define FLASH_PART_SIZE (4*1024*1024) ++#define FLASH_BLOCK_SIZE (128*1024) ++ ++static void armflash_flash_init(void) ++{ ++} ++ ++static void armflash_flash_exit(void) ++{ ++} ++ ++static void armflash_flash_wp(int on) ++{ ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++} ++#endif ++ ++ ++static struct map_info armflash_map = ++{ ++ .name = "AFS", ++ .set_vpp = armflash_set_vpp, ++ .phys = FLASH_BASE, ++}; ++ ++static struct mtd_info *mtd; ++static struct mtd_partition *parts; ++static const char *probes[] = { "RedBoot", "afs", NULL }; ++ ++static int __init armflash_cfi_init(void *base, u_int size) ++{ ++ int ret; ++ ++ armflash_flash_init(); ++ armflash_flash_wp(1); ++ ++ /* ++ * look for CFI based flash parts fitted to this board ++ */ ++ armflash_map.size = size; ++ armflash_map.bankwidth = 4; ++ armflash_map.virt = (void __iomem *) base; ++ ++ simple_map_init(&armflash_map); ++ ++ /* ++ * Also, the CFI layer automatically works out what size ++ * of chips we have, and does the necessary identification ++ * for us automatically. ++ */ ++ mtd = do_map_probe("cfi_probe", &armflash_map); ++ if (!mtd) ++ return -ENXIO; ++ ++ mtd->owner = THIS_MODULE; ++ ++ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0); ++ if (ret > 0) { ++ ret = add_mtd_partitions(mtd, parts, ret); ++ if (ret) ++ printk(KERN_ERR "mtd partition registration " ++ "failed: %d\n", ret); ++ } ++ ++ /* ++ * If we got an error, free all resources. ++ */ ++ if (ret < 0) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ ++ return ret; ++} ++ ++static void armflash_cfi_exit(void) ++{ ++ if (mtd) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ if (parts) ++ kfree(parts); ++} ++ ++static int __init armflash_init(void) ++{ ++ int err = -EBUSY; ++ void *base; ++ ++ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) ++ goto out; ++ ++ base = ioremap(FLASH_BASE, FLASH_SIZE); ++ err = -ENOMEM; ++ if (base == NULL) ++ goto release; ++ ++ err = armflash_cfi_init(base, FLASH_SIZE); ++ if (err) { ++ iounmap(base); ++release: ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ } ++out: ++ return err; ++} ++ ++static void __exit armflash_exit(void) ++{ ++ armflash_cfi_exit(); ++ iounmap((void *)armflash_map.virt); ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ armflash_flash_exit(); ++} ++ ++module_init(armflash_init); ++module_exit(armflash_exit); ++ ++MODULE_AUTHOR("ARM Ltd"); ++MODULE_DESCRIPTION("ARM Integrator CFI map driver"); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/maps/integrator-flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/integrator-flash.c +@@ -1,8 +1,9 @@ + /*====================================================================== + +- drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning ++ drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver + + Copyright (C) 2000 ARM Limited ++ Copyright (C) 2003 Deep Blue Solutions Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -21,7 +22,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id$ ++ $Id$ + + ======================================================================*/ + +@@ -31,268 +32,181 @@ + #include + #include + #include ++#include + #include + + #include + #include + #include + ++#include + #include + #include + #include + +-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); +- +-// board specific stuff - sorry, it should be in arch/arm/mach-*. +-#ifdef CONFIG_ARCH_INTEGRATOR +- +-#define FLASH_BASE INTEGRATOR_FLASH_BASE +-#define FLASH_SIZE INTEGRATOR_FLASH_SIZE +- +-#define FLASH_PART_SIZE 0x400000 +- +-#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +-#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) +-#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) +-#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) +- +-/* +- * Initialise the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Set write enable bit in EBI reg +- */ +-static void armflash_flash_init(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-/* +- * Shutdown the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Clear write enable bit in EBI reg +- */ +-static void armflash_flash_exit(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- /* +- * Clear the write enable bit in system controller EBI register. +- */ +- tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-static void armflash_flash_wp(int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLC; +- else +- reg = SC_CTRLS; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLS; +- else +- reg = SC_CTRLC; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); +-} +-#endif +- + #ifdef CONFIG_ARCH_P720T +- + #define FLASH_BASE (0x04000000) + #define FLASH_SIZE (64*1024*1024) +- +-#define FLASH_PART_SIZE (4*1024*1024) +-#define FLASH_BLOCK_SIZE (128*1024) +- +-static void armflash_flash_init(void) +-{ +-} +- +-static void armflash_flash_exit(void) +-{ +-} +- +-static void armflash_flash_wp(int on) +-{ +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +-} + #endif + +-static __u8 armflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(ofs + map->map_priv_2); +-} +- +-static __u16 armflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(ofs + map->map_priv_2); +-} +- +-static __u32 armflash_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(ofs + map->map_priv_2); +-} ++struct armflash_info { ++ struct flash_platform_data *plat; ++ struct resource *res; ++ struct mtd_partition *parts; ++ struct mtd_info *mtd; ++ struct map_info map; ++}; + +-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++static void armflash_set_vpp(struct map_info *map, int on) + { +- memcpy(to, (void *) (from + map->map_priv_2), len); +-} ++ struct armflash_info *info = container_of(map, struct armflash_info, map); + +-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, adr + map->map_priv_2); ++ if (info->plat && info->plat->set_vpp) ++ info->plat->set_vpp(on); + } + +-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, adr + map->map_priv_2); +-} ++static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; + +-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr) ++static int armflash_probe(struct device *_dev) + { +- writel(d, adr + map->map_priv_2); +-} ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct resource *res = dev->resource; ++ unsigned int size = res->end - res->start + 1; ++ struct armflash_info *info; ++ int err; ++ void __iomem *base; + +-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (to + map->map_priv_2), from, len); +-} ++ info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); ++ if (!info) { ++ err = -ENOMEM; ++ goto out; ++ } + +-static struct map_info armflash_map = +-{ +- name: "AFS", +- read8: armflash_read8, +- read16: armflash_read16, +- read32: armflash_read32, +- copy_from: armflash_copy_from, +- write8: armflash_write8, +- write16: armflash_write16, +- write32: armflash_write32, +- copy_to: armflash_copy_to, +- set_vpp: armflash_set_vpp, +-}; ++ memset(info, 0, sizeof(struct armflash_info)); + +-static struct mtd_info *mtd; +-static struct mtd_partition *parts; ++ info->plat = plat; ++ if (plat && plat->init) { ++ err = plat->init(); ++ if (err) ++ goto no_resource; ++ } + +-static int __init armflash_cfi_init(void *base, u_int size) +-{ +- int ret; ++ info->res = request_mem_region(res->start, size, "armflash"); ++ if (!info->res) { ++ err = -EBUSY; ++ goto no_resource; ++ } + +- armflash_flash_init(); +- armflash_flash_wp(1); ++ base = ioremap(res->start, size); ++ if (!base) { ++ err = -ENOMEM; ++ goto no_mem; ++ } + + /* + * look for CFI based flash parts fitted to this board + */ +- armflash_map.size = size; +- armflash_map.buswidth = 4; +- armflash_map.map_priv_2 = (unsigned long) base; ++ info->map.size = size; ++ info->map.bankwidth = plat->width; ++ info->map.phys = res->start; ++ info->map.virt = base; ++ info->map.name = dev->dev.bus_id; ++ info->map.set_vpp = armflash_set_vpp; ++ ++ simple_map_init(&info->map); + + /* + * Also, the CFI layer automatically works out what size + * of chips we have, and does the necessary identification + * for us automatically. + */ +- mtd = do_map_probe("cfi_probe", &armflash_map); +- if (!mtd) +- return -ENXIO; ++ info->mtd = do_map_probe(plat->map_name, &info->map); ++ if (!info->mtd) { ++ err = -ENXIO; ++ goto no_device; ++ } + +- mtd->module = THIS_MODULE; ++ info->mtd->owner = THIS_MODULE; + +- ret = parse_afs_partitions(mtd, &parts); +- if (ret > 0) { +- ret = add_mtd_partitions(mtd, parts, ret); +- if (ret) +- printk(KERN_ERR "mtd partition registration " +- "failed: %d\n", ret); ++ err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->parts, err); ++ if (err) ++ printk(KERN_ERR ++ "mtd partition registration failed: %d\n", err); + } + ++ if (err == 0) ++ dev_set_drvdata(&dev->dev, info); ++ + /* + * If we got an error, free all resources. + */ +- if (ret < 0) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ if (err < 0) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); + } ++ if (info->parts) ++ kfree(info->parts); + +- return ret; +-} +- +-static void armflash_cfi_exit(void) +-{ +- if (mtd) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ no_device: ++ iounmap(base); ++ no_mem: ++ release_mem_region(res->start, size); ++ no_resource: ++ if (plat && plat->exit) ++ plat->exit(); ++ kfree(info); + } +- if (parts) +- kfree(parts); ++ out: ++ return err; + } + +-static int __init armflash_init(void) ++static int armflash_remove(struct device *_dev) + { +- int err = -EBUSY; +- void *base; ++ struct platform_device *dev = to_platform_device(_dev); ++ struct armflash_info *info = dev_get_drvdata(&dev->dev); + +- if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) +- goto out; ++ dev_set_drvdata(&dev->dev, NULL); + +- base = ioremap(FLASH_BASE, FLASH_SIZE); +- err = -ENOMEM; +- if (base == NULL) +- goto release; ++ if (info) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ if (info->parts) ++ kfree(info->parts); + +- err = armflash_cfi_init(base, FLASH_SIZE); +- if (err) { +- iounmap(base); +-release: +- release_mem_region(FLASH_BASE, FLASH_SIZE); ++ iounmap(info->map.virt); ++ release_resource(info->res); ++ kfree(info->res); ++ ++ if (info->plat && info->plat->exit) ++ info->plat->exit(); ++ ++ kfree(info); + } +-out: +- return err; ++ ++ return 0; ++} ++ ++static struct device_driver armflash_driver = { ++ .name = "armflash", ++ .bus = &platform_bus_type, ++ .probe = armflash_probe, ++ .remove = armflash_remove, ++}; ++ ++static int __init armflash_init(void) ++{ ++ return driver_register(&armflash_driver); + } + + static void __exit armflash_exit(void) + { +- armflash_cfi_exit(); +- iounmap((void *)armflash_map.map_priv_2); +- release_mem_region(FLASH_BASE, FLASH_SIZE); +- armflash_flash_exit(); ++ driver_unregister(&armflash_driver); + } + + module_init(armflash_init); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ipaq-flash.c +@@ -0,0 +1,464 @@ ++/* ++ * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based) ++ * ++ * (C) 2000 Nicolas Pitre ++ * (C) 2002 Hewlett-Packard Company ++ * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#ifdef CONFIG_MTD_CONCAT ++#include ++#endif ++ ++#include ++#include ++#include ++ ++ ++#ifndef CONFIG_IPAQ_HANDHELD ++#error This is for iPAQ Handhelds only ++#endif ++#ifdef CONFIG_SA1100_JORNADA56X ++ ++static void jornada56x_set_vpp(struct map_info *map, int vpp) ++{ ++ if (vpp) ++ GPSR = GPIO_GPIO26; ++ else ++ GPCR = GPIO_GPIO26; ++ GPDR |= GPIO_GPIO26; ++} ++ ++#endif ++ ++#ifdef CONFIG_SA1100_JORNADA720 ++ ++static void jornada720_set_vpp(struct map_info *map, int vpp) ++{ ++ if (vpp) ++ PPSR |= 0x80; ++ else ++ PPSR &= ~0x80; ++ PPDR |= 0x80; ++} ++ ++#endif ++ ++#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */ ++ ++#define IPAQ_MAP_INIT(X) \ ++ { \ ++ name: "IPAQ flash " X, \ ++ } ++ ++ ++static struct map_info ipaq_map[MAX_IPAQ_CS] = { ++ IPAQ_MAP_INIT("bank 1"), ++ IPAQ_MAP_INIT("bank 2") ++}; ++ ++static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = { ++ NULL, ++ NULL ++}; ++ ++/* ++ * Here are partition information for all known IPAQ-based devices. ++ * See include/linux/mtd/partitions.h for definition of the mtd_partition ++ * structure. ++ * ++ * The *_max_flash_size is the maximum possible mapped flash size which ++ * is not necessarily the actual flash size. It must be no more than ++ * the value specified in the "struct map_desc *_io_desc" mapping ++ * definition for the corresponding machine. ++ * ++ * Please keep these in alphabetical order, and formatted as per existing ++ * entries. Thanks. ++ */ ++ ++#ifdef CONFIG_IPAQ_HANDHELD ++static unsigned long h3xxx_max_flash_size = 0x04000000; ++static struct mtd_partition h3xxx_partitions[] = { ++ { ++ name: "H3XXX boot firmware", ++#ifndef CONFIG_LAB ++ size: 0x00040000, ++#else ++ size: 0x00080000, ++#endif ++ offset: 0, ++#ifndef CONFIG_LAB ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++#endif ++ }, ++ { ++ name: "H3XXX root jffs2", ++#ifndef CONFIG_LAB ++ size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */ ++ offset: 0x00040000, ++#else ++ size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */ ++ offset: 0x00080000, ++#endif ++ }, ++ { ++ name: "asset", ++ size: 0x40000, ++ offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */ ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ } ++}; ++ ++#ifndef CONFIG_MTD_CONCAT ++static struct mtd_partition h3xxx_partitions_bank2[] = { ++ /* this is used only on 2 CS machines when concat is not present */ ++ { ++ name: "second H3XXX root jffs2", ++ size: 0x1000000 - 0x40000, /* Warning, this is fixed later */ ++ offset: 0x00000000, ++ }, ++ { ++ name: "second asset", ++ size: 0x40000, ++ offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */ ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ } ++}; ++#endif ++ ++static DEFINE_SPINLOCK(ipaq_vpp_lock); ++ ++static void h3xxx_set_vpp(struct map_info *map, int vpp) ++{ ++ static int nest = 0; ++ ++ spin_lock(&ipaq_vpp_lock); ++ if (vpp) ++ nest++; ++ else ++ nest--; ++ if (nest) ++ assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1); ++ else ++ assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0); ++ spin_unlock(&ipaq_vpp_lock); ++} ++ ++#endif ++ ++#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720) ++static unsigned long jornada_max_flash_size = 0x02000000; ++static struct mtd_partition jornada_partitions[] = { ++ { ++ name: "Jornada boot firmware", ++ size: 0x00040000, ++ offset: 0, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "Jornada root jffs2", ++ size: MTDPART_SIZ_FULL, ++ offset: 0x00040000, ++ } ++}; ++#endif ++ ++ ++static struct mtd_partition *parsed_parts; ++static struct mtd_info *mymtd; ++ ++static unsigned long cs_phys[] = { ++#ifdef CONFIG_ARCH_SA1100 ++ SA1100_CS0_PHYS, ++ SA1100_CS1_PHYS, ++ SA1100_CS2_PHYS, ++ SA1100_CS3_PHYS, ++ SA1100_CS4_PHYS, ++ SA1100_CS5_PHYS, ++#else ++ PXA_CS0_PHYS, ++ PXA_CS1_PHYS, ++ PXA_CS2_PHYS, ++ PXA_CS3_PHYS, ++ PXA_CS4_PHYS, ++ PXA_CS5_PHYS, ++#endif ++}; ++ ++static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; ++ ++static int __init h1900_special_case(void); ++ ++int __init ipaq_mtd_init(void) ++{ ++ struct mtd_partition *parts = NULL; ++ int nb_parts = 0; ++ int parsed_nr_parts = 0; ++ const char *part_type; ++ int i; /* used when we have >1 flash chips */ ++ unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */ ++ ++ /* Default flash bankwidth */ ++ // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; ++ ++ if (machine_is_h1900()) ++ { ++ /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */ ++ return h1900_special_case(); ++ } ++ ++ if (machine_is_h3100() || machine_is_h1900()) ++ for(i=0; isize); ++ ++ /* do we really need this debugging? --joshua 20030703 */ ++ // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]); ++ my_sub_mtd[i]->owner = THIS_MODULE; ++ tot_flashsize += my_sub_mtd[i]->size; ++ } ++#ifdef CONFIG_MTD_CONCAT ++ /* fix the asset location */ ++# ifdef CONFIG_LAB ++ h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */; ++# else ++ h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000; ++# endif ++ h3xxx_partitions[2].offset = tot_flashsize - 0x40000; ++ /* and concat the devices */ ++ mymtd = mtd_concat_create(&my_sub_mtd[0], i, ++ "ipaq"); ++ if (!mymtd) { ++ printk("Cannot create iPAQ concat device\n"); ++ return -ENXIO; ++ } ++#else ++ mymtd = my_sub_mtd[0]; ++ ++ /* ++ *In the very near future, command line partition parsing ++ * will use the device name as 'mtd-id' instead of a value ++ * passed to the parse_cmdline_partitions() routine. Since ++ * the bootldr says 'ipaq', make sure it continues to work. ++ */ ++ mymtd->name = "ipaq"; ++ ++ if ((machine_is_h3600())) { ++# ifdef CONFIG_LAB ++ h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000; ++# else ++ h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000; ++# endif ++ nb_parts = 2; ++ } else { ++# ifdef CONFIG_LAB ++ h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */ ++# else ++ h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000; ++# endif ++ h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000; ++ } ++ ++ if (my_sub_mtd[1]) { ++# ifdef CONFIG_LAB ++ h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000; ++# else ++ h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000; ++# endif ++ h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000; ++ } ++#endif ++ } ++ else { ++ /* ++ * Now let's probe for the actual flash. Do it here since ++ * specific machine settings might have been set above. ++ */ ++ printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt); ++ mymtd = do_map_probe("cfi_probe", &ipaq_map[0]); ++ if (!mymtd) ++ return -ENXIO; ++ mymtd->owner = THIS_MODULE; ++ } ++ ++ ++ /* ++ * Dynamic partition selection stuff (might override the static ones) ++ */ ++ ++ i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0); ++ ++ if (i > 0) { ++ nb_parts = parsed_nr_parts = i; ++ parts = parsed_parts; ++ part_type = "dynamic"; ++ } ++ ++ if (!parts) { ++ printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n"); ++ add_mtd_device(mymtd); ++#ifndef CONFIG_MTD_CONCAT ++ if (my_sub_mtd[1]) ++ add_mtd_device(my_sub_mtd[1]); ++#endif ++ } else { ++ printk(KERN_NOTICE "Using %s partition definition\n", part_type); ++ add_mtd_partitions(mymtd, parts, nb_parts); ++#ifndef CONFIG_MTD_CONCAT ++ if (my_sub_mtd[1]) ++ add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2)); ++#endif ++ } ++ ++ return 0; ++} ++ ++static void __exit ipaq_mtd_cleanup(void) ++{ ++ int i; ++ ++ if (mymtd) { ++ del_mtd_partitions(mymtd); ++#ifndef CONFIG_MTD_CONCAT ++ if (my_sub_mtd[1]) ++ del_mtd_partitions(my_sub_mtd[1]); ++#endif ++ map_destroy(mymtd); ++#ifdef CONFIG_MTD_CONCAT ++ for(i=0; i + #include + #include ++#include ++#include + #include + #include + #include +@@ -26,127 +28,72 @@ + + static struct mtd_info *mymtd; + +-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- + static struct map_info iq80310_map = { +- name: "IQ80310 flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: iq80310_read8, +- read16: iq80310_read16, +- read32: iq80310_read32, +- copy_from: iq80310_copy_from, +- write8: iq80310_write8, +- write16: iq80310_write16, +- write32: iq80310_write32, +- copy_to: iq80310_copy_to ++ .name = "IQ80310 flash", ++ .size = WINDOW_SIZE, ++ .bankwidth = BUSWIDTH, ++ .phys = WINDOW_ADDR + }; + + static struct mtd_partition iq80310_partitions[4] = { + { +- name: "Firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ +- name: "Kernel", +- size: 0x000a0000, +- offset: 0x00080000, ++ .name = "Kernel", ++ .size = 0x000a0000, ++ .offset = 0x00080000, + },{ +- name: "Filesystem", +- size: 0x00600000, +- offset: 0x00120000 ++ .name = "Filesystem", ++ .size = 0x00600000, ++ .offset = 0x00120000 + },{ +- name: "RedBoot", +- size: 0x000e0000, +- offset: 0x00720000, +- mask_flags: MTD_WRITEABLE ++ .name = "RedBoot", ++ .size = 0x000e0000, ++ .offset = 0x00720000, ++ .mask_flags = MTD_WRITEABLE + } + }; + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + static struct mtd_info *mymtd; + static struct mtd_partition *parsed_parts; +- +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + static int __init init_iq80310(void) + { + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; +- char *part_type = "static"; ++ int ret; + +- iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); +- if (!iq80310_map.map_priv_1) { ++ iq80310_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ if (!iq80310_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&iq80310_map); ++ + mymtd = do_map_probe("cfi_probe", &iq80310_map); + if (!mymtd) { +- iounmap((void *)iq80310_map.map_priv_1); ++ iounmap((void *)iq80310_map.virt); + return -ENXIO; + } +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); + +- if (ret > 0) { +- part_type = "RedBoot"; ++ if (ret > 0) + parsed_nr_parts = ret; +- } +- } +-#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } else { + parts = iq80310_partitions; +- nb_parts = NB_OF(iq80310_partitions); ++ nb_parts = ARRAY_SIZE(iq80310_partitions); + } +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; + } +@@ -159,8 +106,8 @@ + if (parsed_parts) + kfree(parsed_parts); + } +- if (iq80310_map.map_priv_1) +- iounmap((void *)iq80310_map.map_priv_1); ++ if (iq80310_map.virt) ++ iounmap((void *)iq80310_map.virt); + } + + module_init(init_iq80310); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ixp2000.c +@@ -0,0 +1,280 @@ ++/* ++ * $Id$ ++ * ++ * drivers/mtd/maps/ixp2000.c ++ * ++ * Mapping for the Intel XScale IXP2000 based systems ++ * ++ * Copyright (C) 2002 Intel Corp. ++ * Copyright (C) 2003-2004 MontaVista Software, Inc. ++ * ++ * Original Author: Naeem M Afzal ++ * Maintainer: Deepak Saxena ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++struct ixp2000_flash_info { ++ struct mtd_info *mtd; ++ struct map_info map; ++ struct mtd_partition *partitions; ++ struct resource *res; ++ int nr_banks; ++}; ++ ++static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) ++{ ++ unsigned long (*set_bank)(unsigned long) = ++ (unsigned long(*)(unsigned long))map->map_priv_2; ++ ++ return (set_bank ? set_bank(ofs) : ofs); ++} ++ ++#ifdef __ARMEB__ ++/* ++ * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which ++ * causes the lower address bits to be XORed with 0x11 on 8 bit accesses ++ * and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44. ++ */ ++static int erratum44_workaround = 0; ++ ++static inline unsigned long address_fix8_write(unsigned long addr) ++{ ++ if (erratum44_workaround) { ++ return (addr ^ 3); ++ } ++ return addr; ++} ++#else ++ ++#define address_fix8_write(x) (x) ++#endif ++ ++static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs) ++{ ++ map_word val; ++ ++ val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs))); ++ return val; ++} ++ ++/* ++ * We can't use the standard memcpy due to the broken SlowPort ++ * address translation on rev A0 and A1 silicon and the fact that ++ * we have banked flash. ++ */ ++static void ixp2000_flash_copy_from(struct map_info *map, void *to, ++ unsigned long from, ssize_t len) ++{ ++ from = flash_bank_setup(map, from); ++ while(len--) ++ *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); ++} ++ ++static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs) ++{ ++ *(__u8 *) (address_fix8_write(map->map_priv_1 + ++ flash_bank_setup(map, ofs))) = d.x[0]; ++} ++ ++static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to, ++ const void *from, ssize_t len) ++{ ++ to = flash_bank_setup(map, to); ++ while(len--) { ++ unsigned long tmp = address_fix8_write(map->map_priv_1 + to++); ++ *(__u8 *)(tmp) = *(__u8 *)(from++); ++ } ++} ++ ++ ++static int ixp2000_flash_remove(struct device *_dev) ++{ ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct ixp2000_flash_info *info = dev_get_drvdata(&dev->dev); ++ ++ dev_set_drvdata(&dev->dev, NULL); ++ ++ if(!info) ++ return 0; ++ ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ if (info->map.map_priv_1) ++ iounmap((void *) info->map.map_priv_1); ++ ++ if (info->partitions) { ++ kfree(info->partitions); } ++ ++ if (info->res) { ++ release_resource(info->res); ++ kfree(info->res); ++ } ++ ++ if (plat->exit) ++ plat->exit(); ++ ++ return 0; ++} ++ ++ ++static int ixp2000_flash_probe(struct device *_dev) ++{ ++ static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ struct platform_device *dev = to_platform_device(_dev); ++ struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; ++ struct flash_platform_data *plat; ++ struct ixp2000_flash_info *info; ++ unsigned long window_size; ++ int err = -1; ++ ++ if (!ixp_data) ++ return -ENODEV; ++ ++ plat = ixp_data->platform_data; ++ if (!plat) ++ return -ENODEV; ++ ++ window_size = dev->resource->end - dev->resource->start + 1; ++ dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", ++ ixp_data->nr_banks, ((u32)window_size >> 20)); ++ ++ if (plat->width != 1) { ++ dev_err(_dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n", ++ plat->width * 8); ++ return -EIO; ++ } ++ ++ info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); ++ if(!info) { ++ err = -ENOMEM; ++ goto Error; ++ } ++ memzero(info, sizeof(struct ixp2000_flash_info)); ++ ++ dev_set_drvdata(&dev->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->nr_banks = ixp_data->nr_banks; ++ info->map.size = ixp_data->nr_banks * window_size; ++ info->map.bankwidth = 1; ++ ++ /* ++ * map_priv_2 is used to store a ptr to to the bank_setup routine ++ */ ++ info->map.map_priv_2 = (void __iomem *) ixp_data->bank_setup; ++ ++ info->map.name = dev->dev.bus_id; ++ info->map.read = ixp2000_flash_read8; ++ info->map.write = ixp2000_flash_write8; ++ info->map.copy_from = ixp2000_flash_copy_from; ++ info->map.copy_to = ixp2000_flash_copy_to; ++ ++ info->res = request_mem_region(dev->resource->start, ++ dev->resource->end - dev->resource->start + 1, ++ dev->dev.bus_id); ++ if (!info->res) { ++ dev_err(_dev, "Could not reserve memory region\n"); ++ err = -ENOMEM; ++ goto Error; ++ } ++ ++ info->map.map_priv_1 = ioremap(dev->resource->start, ++ dev->resource->end - dev->resource->start + 1); ++ if (!info->map.map_priv_1) { ++ dev_err(_dev, "Failed to ioremap flash region\n"); ++ err = -EIO; ++ goto Error; ++ } ++ ++ /* ++ * Setup read mode for FLASH ++ */ ++ *IXP2000_SLOWPORT_FRM = 1; ++ ++#if defined(__ARMEB__) ++ /* ++ * Enable erratum 44 workaround for NPUs with broken slowport ++ */ ++ ++ erratum44_workaround = ixp2000_has_broken_slowport(); ++ dev_info(_dev, "Erratum 44 workaround %s\n", ++ erratum44_workaround ? "enabled" : "disabled"); ++#endif ++ ++ info->mtd = do_map_probe(plat->map_name, &info->map); ++ if (!info->mtd) { ++ dev_err(_dev, "map_probe failed\n"); ++ err = -ENXIO; ++ goto Error; ++ } ++ info->mtd->owner = THIS_MODULE; ++ ++ err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->partitions, err); ++ if(err) ++ dev_err(_dev, "Could not parse partitions\n"); ++ } ++ ++ if (err) ++ goto Error; ++ ++ return 0; ++ ++Error: ++ ixp2000_flash_remove(_dev); ++ return err; ++} ++ ++static struct device_driver ixp2000_flash_driver = { ++ .name = "IXP2000-Flash", ++ .bus = &platform_bus_type, ++ .probe = &ixp2000_flash_probe, ++ .remove = &ixp2000_flash_remove ++}; ++ ++static int __init ixp2000_flash_init(void) ++{ ++ return driver_register(&ixp2000_flash_driver); ++} ++ ++static void __exit ixp2000_flash_exit(void) ++{ ++ driver_unregister(&ixp2000_flash_driver); ++} ++ ++module_init(ixp2000_flash_init); ++module_exit(ixp2000_flash_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Deepak Saxena "); ++ +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ixp4xx.c +@@ -0,0 +1,259 @@ ++/* ++ * $Id$ ++ * ++ * 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 ++ * ++ * Copyright (C) 2002 Intel Corporation ++ * Copyright (C) 2003-2004 MontaVista Software, Inc. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#ifndef __ARMEB__ ++#define BYTE0(h) ((h) & 0xFF) ++#define BYTE1(h) (((h) >> 8) & 0xFF) ++#else ++#define BYTE0(h) (((h) >> 8) & 0xFF) ++#define BYTE1(h) ((h) & 0xFF) ++#endif ++ ++static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) ++{ ++ map_word val; ++ val.x[0] = *(__u16 *) (map->map_priv_1 + 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) ++{ ++ int i; ++ u8 *dest = (u8 *) to; ++ u16 *src = (u16 *) (map->map_priv_1 + from); ++ u16 data; ++ ++ for (i = 0; i < (len / 2); i++) { ++ data = src[i]; ++ dest[i * 2] = BYTE0(data); ++ dest[i * 2 + 1] = BYTE1(data); ++ } ++ ++ if (len & 1) ++ dest[len - 1] = BYTE0(src[i]); ++} ++ ++/* ++ * 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)) ++ *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; ++} ++ ++/* ++ * Fast write16 function without the probing check above ++ */ ++static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) ++{ ++ *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; ++} ++ ++struct ixp4xx_flash_info { ++ struct mtd_info *mtd; ++ struct map_info map; ++ struct mtd_partition *partitions; ++ struct resource *res; ++}; ++ ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static int ixp4xx_flash_remove(struct device *_dev) ++{ ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev); ++ map_word d; ++ ++ dev_set_drvdata(&dev->dev, NULL); ++ ++ if(!info) ++ return 0; ++ ++ /* ++ * This is required for a soft reboot to work. ++ */ ++ d.x[0] = 0xff; ++ ixp4xx_write16(&info->map, d, 0x55 * 0x2); ++ ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ if (info->map.map_priv_1) ++ iounmap((void *) info->map.map_priv_1); ++ ++ if (info->partitions) ++ kfree(info->partitions); ++ ++ if (info->res) { ++ release_resource(info->res); ++ kfree(info->res); ++ } ++ ++ if (plat->exit) ++ plat->exit(); ++ ++ /* Disable flash write */ ++ *IXP4XX_EXP_CS0 &= ~IXP4XX_FLASH_WRITABLE; ++ ++ return 0; ++} ++ ++static int ixp4xx_flash_probe(struct device *_dev) ++{ ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct ixp4xx_flash_info *info; ++ int err = -1; ++ ++ if (!plat) ++ return -ENODEV; ++ ++ if (plat->init) { ++ err = plat->init(); ++ if (err) ++ return err; ++ } ++ ++ info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); ++ if(!info) { ++ err = -ENOMEM; ++ goto Error; ++ } ++ memzero(info, sizeof(struct ixp4xx_flash_info)); ++ ++ dev_set_drvdata(&dev->dev, info); ++ ++ /* ++ * Enable flash write ++ * TODO: Move this out to board specific code ++ */ ++ *IXP4XX_EXP_CS0 |= IXP4XX_FLASH_WRITABLE; ++ ++ /* ++ * 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 = dev->resource->end - dev->resource->start + 1; ++ ++ /* ++ * 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->dev.bus_id; ++ info->map.read = ixp4xx_read16, ++ info->map.write = ixp4xx_probe_write16, ++ info->map.copy_from = ixp4xx_copy_from, ++ ++ info->res = request_mem_region(dev->resource->start, ++ dev->resource->end - dev->resource->start + 1, ++ "IXP4XXFlash"); ++ if (!info->res) { ++ printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n"); ++ err = -ENOMEM; ++ goto Error; ++ } ++ ++ info->map.map_priv_1 = ioremap(dev->resource->start, ++ dev->resource->end - dev->resource->start + 1); ++ if (!info->map.map_priv_1) { ++ printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); ++ err = -EIO; ++ 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 = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->partitions, err); ++ if(err) ++ printk(KERN_ERR "Could not parse partitions\n"); ++ } ++ ++ if (err) ++ goto Error; ++ ++ return 0; ++ ++Error: ++ ixp4xx_flash_remove(_dev); ++ return err; ++} ++ ++static struct device_driver ixp4xx_flash_driver = { ++ .name = "IXP4XX-Flash", ++ .bus = &platform_bus_type, ++ .probe = ixp4xx_flash_probe, ++ .remove = ixp4xx_flash_remove, ++}; ++ ++static int __init ixp4xx_flash_init(void) ++{ ++ return driver_register(&ixp4xx_flash_driver); ++} ++ ++static void __exit ixp4xx_flash_exit(void) ++{ ++ driver_unregister(&ixp4xx_flash_driver); ++} ++ ++ ++module_init(ixp4xx_flash_init); ++module_exit(ixp4xx_flash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems") ++MODULE_AUTHOR("Deepak Saxena"); ++ +--- linux-2.4.21/drivers/mtd/maps/l440gx.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/l440gx.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * BIOS Flash chip on Intel 440GX board. + * +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,51 +28,9 @@ + + static struct mtd_info *mymtd; + +-__u8 l440gx_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 l440gx_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 l440gx_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + /* Is this really the vpp port? */ +-void l440gx_set_vpp(struct map_info *map, int vpp) ++static void l440gx_set_vpp(struct map_info *map, int vpp) + { + unsigned long l; + +@@ -84,23 +43,16 @@ + outl(l, VPP_PORT); + } + +-struct map_info l440gx_map = { +- name: "L440GX BIOS", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: l440gx_read8, +- read16: l440gx_read16, +- read32: l440gx_read32, +- copy_from: l440gx_copy_from, +- write8: l440gx_write8, +- write16: l440gx_write16, +- write32: l440gx_write32, +- copy_to: l440gx_copy_to, ++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 ++ .set_vpp = l440gx_set_vpp + #endif + }; + +@@ -113,7 +65,6 @@ + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, NULL); + +- + pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + +@@ -122,15 +73,14 @@ + return -ENODEV; + } + ++ l440gx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); + +- l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); +- +- if (!l440gx_map.map_priv_1) { ++ if (!l440gx_map.virt) { + printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); + return -ENOMEM; + } +- +- printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1); ++ simple_map_init(&l440gx_map); ++ printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); + + /* Setup the pm iobase resource + * This code should move into some kind of generic bridge +@@ -153,7 +103,7 @@ + /* Allocate the resource region */ + if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { + printk(KERN_WARNING "Could not allocate pm iobase resource\n"); +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap(l440gx_map.virt); + return -ENXIO; + } + } +@@ -181,13 +131,13 @@ + mymtd = do_map_probe("map_rom", &l440gx_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap(l440gx_map.virt); + return -ENXIO; + } + +@@ -196,7 +146,7 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap(l440gx_map.virt); + } + + module_init(init_l440gx); +--- linux-2.4.21/drivers/mtd/maps/lasat.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/lasat.c +@@ -1,111 +1,73 @@ + /* +- * Flash device on lasat 100 and 200 boards ++ * Flash device on Lasat 100 and 200 boards + * +- * Presumably (C) 2002 Brian Murphy or whoever he +- * works for. ++ * (C) 2002 Brian Murphy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * +- * $Id$ ++ * $Id$ + * + */ + + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include +-#include +- +-static struct mtd_info *mymtd; +- +-static __u8 sp_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sp_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sp_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} + +-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++static struct mtd_info *lasat_mtd; + +-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++static struct mtd_partition partition_info[LASAT_MTD_LAST]; ++static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; + +-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++static void lasat_set_vpp(struct map_info *map, int vpp) + { +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (vpp) ++ *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ else ++ *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit); + } + +-static struct map_info sp_map = { +- .name = "SP flash", +- .buswidth = 4, +- .read8 = sp_read8, +- .read16 = sp_read16, +- .read32 = sp_read32, +- .copy_from = sp_copy_from, +- .write8 = sp_write8, +- .write16 = sp_write16, +- .write32 = sp_write32, +- .copy_to = sp_copy_to ++static struct map_info lasat_map = { ++ .name = "LASAT flash", ++ .bankwidth = 4, ++ .set_vpp = lasat_set_vpp + }; + +-static struct mtd_partition partition_info[LASAT_MTD_LAST]; +-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; +- +-static int __init init_sp(void) ++static int __init init_lasat(void) + { + int i; +- /* this does not play well with the old flash code which +- * protects and uprotects the flash when necessary */ ++ /* since we use AMD chips and set_vpp is not implimented ++ * for these (yet) we still have to permanently enable flash write */ + printk(KERN_NOTICE "Unprotecting flash\n"); +- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ ENABLE_VPP((&lasat_map)); + +- sp_map.map_priv_1 = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); +- sp_map.size = lasat_board_info.li_flash_size; ++ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); ++ lasat_map.virt = ioremap_nocache( ++ lasat_map.phys, lasat_board_info.li_flash_size); ++ lasat_map.size = lasat_board_info.li_flash_size; + +- printk(KERN_NOTICE "sp flash device: %lx at %lx\n", +- sp_map.size, sp_map.map_priv_1); ++ simple_map_init(&lasat_map); + + for (i=0; i < LASAT_MTD_LAST; i++) + partition_info[i].name = lasat_mtd_partnames[i]; + +- mymtd = do_map_probe("cfi_probe", &sp_map); +- if (mymtd) { ++ lasat_mtd = do_map_probe("cfi_probe", &lasat_map); ++ ++ if (!lasat_mtd) ++ lasat_mtd = do_map_probe("jedec_probe", &lasat_map); ++ ++ if (lasat_mtd) { + u32 size, offset = 0; + +- mymtd->module = THIS_MODULE; ++ lasat_mtd->owner = THIS_MODULE; + + for (i=0; i < LASAT_MTD_LAST; i++) { + size = lasat_flash_partition_size(i); +@@ -114,26 +76,26 @@ + offset += size; + } + +- add_mtd_partitions( mymtd, partition_info, LASAT_MTD_LAST ); ++ add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST ); + return 0; + } + + return -ENXIO; + } + +-static void __exit cleanup_sp(void) ++static void __exit cleanup_lasat(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); ++ if (lasat_mtd) { ++ del_mtd_partitions(lasat_mtd); ++ map_destroy(lasat_mtd); + } +- if (sp_map.map_priv_1) { +- sp_map.map_priv_1 = 0; ++ if (lasat_map.virt) { ++ lasat_map.virt = 0; + } + } + +-module_init(init_sp); +-module_exit(cleanup_sp); ++module_init(init_lasat); ++module_exit(cleanup_lasat); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Brian Murphy "); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/lubbock-flash.c +@@ -0,0 +1,168 @@ ++/* ++ * $Id$ ++ * ++ * Map driver for the Lubbock developer platform. ++ * ++ * Author: Nicolas Pitre ++ * Copyright: (C) 2001 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define ROM_ADDR 0x00000000 ++#define FLASH_ADDR 0x04000000 ++ ++#define WINDOW_SIZE 64*1024*1024 ++ ++static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) ++{ ++ consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); ++} ++ ++static struct map_info lubbock_maps[2] = { { ++ .size = WINDOW_SIZE, ++ .phys = 0x00000000, ++ .inval_cache = lubbock_map_inval_cache, ++}, { ++ .size = WINDOW_SIZE, ++ .phys = 0x04000000, ++ .inval_cache = lubbock_map_inval_cache, ++} }; ++ ++static struct mtd_partition lubbock_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ },{ ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00040000, ++ },{ ++ .name = "Filesystem", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00140000 ++ } ++}; ++ ++static struct mtd_info *mymtds[2]; ++static struct mtd_partition *parsed_parts[2]; ++static int nr_parsed_parts[2]; ++ ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static int __init init_lubbock(void) ++{ ++ int flashboot = (LUB_CONF_SWITCHES & 1); ++ int ret = 0, i; ++ ++ lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth = ++ (BOOT_DEF & 1) ? 2 : 4; ++ ++ /* Compensate for the nROMBT switch which swaps the flash banks */ ++ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", ++ flashboot?"Flash":"ROM", flashboot); ++ ++ lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; ++ lubbock_maps[flashboot].name = "Lubbock Boot ROM"; ++ ++ for (i = 0; i < 2; i++) { ++ lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE); ++ if (!lubbock_maps[i].virt) { ++ printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); ++ if (!ret) ++ ret = -ENOMEM; ++ continue; ++ } ++ lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE); ++ if (!lubbock_maps[i].cached) ++ printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); ++ simple_map_init(&lubbock_maps[i]); ++ ++ printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", ++ lubbock_maps[i].name, lubbock_maps[i].phys, ++ lubbock_maps[i].bankwidth * 8); ++ ++ mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); ++ ++ if (!mymtds[i]) { ++ iounmap((void *)lubbock_maps[i].virt); ++ if (lubbock_maps[i].cached) ++ iounmap(lubbock_maps[i].cached); ++ if (!ret) ++ ret = -EIO; ++ continue; ++ } ++ mymtds[i]->owner = THIS_MODULE; ++ ++ ret = parse_mtd_partitions(mymtds[i], probes, ++ &parsed_parts[i], 0); ++ ++ if (ret > 0) ++ nr_parsed_parts[i] = ret; ++ } ++ ++ if (!mymtds[0] && !mymtds[1]) ++ return ret; ++ ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) { ++ printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); ++ } else if (nr_parsed_parts[i]) { ++ add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); ++ } else if (!i) { ++ printk("Using static partitions on %s\n", lubbock_maps[i].name); ++ add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); ++ } else { ++ printk("Registering %s as whole device\n", lubbock_maps[i].name); ++ add_mtd_device(mymtds[i]); ++ } ++ } ++ return 0; ++} ++ ++static void __exit cleanup_lubbock(void) ++{ ++ int i; ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) ++ continue; ++ ++ if (nr_parsed_parts[i] || !i) ++ del_mtd_partitions(mymtds[i]); ++ else ++ del_mtd_device(mymtds[i]); ++ ++ map_destroy(mymtds[i]); ++ iounmap((void *)lubbock_maps[i].virt); ++ if (lubbock_maps[i].cached) ++ iounmap(lubbock_maps[i].cached); ++ ++ if (parsed_parts[i]) ++ kfree(parsed_parts[i]); ++ } ++} ++ ++module_init(init_lubbock); ++module_exit(cleanup_lubbock); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nicolas Pitre "); ++MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/map_funcs.c +@@ -0,0 +1,44 @@ ++/* ++ * $Id$ ++ * ++ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS ++ * is enabled. ++ */ ++ ++#include ++#include ++ ++#include ++ ++static map_word simple_map_read(struct map_info *map, unsigned long ofs) ++{ ++ return inline_map_read(map, ofs); ++} ++ ++static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs) ++{ ++ inline_map_write(map, datum, ofs); ++} ++ ++static void 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 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"); +--- linux-2.4.21/drivers/mtd/maps/mbx860.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/mbx860.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Handle mapping of the flash on MBX860 boards + * +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,91 +37,46 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "MBX flash BOOT partition", +- offset: 0, +- size: BOOT_PARTITION_SIZE_KiB*1024 }, +- { name: "MBX flash DATA partition", +- offset: BOOT_PARTITION_SIZE_KiB*1024, +- size: (KERNEL_PARTITION_SIZE_KiB)*1024 }, +- { name: "MBX flash APPLICATION partition", +- offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } ++ { .name = "MBX flash BOOT partition", ++ .offset = 0, ++ .size = BOOT_PARTITION_SIZE_KiB*1024 }, ++ { .name = "MBX flash DATA partition", ++ .offset = BOOT_PARTITION_SIZE_KiB*1024, ++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024 }, ++ { .name = "MBX flash APPLICATION partition", ++ .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } + }; + + + static struct mtd_info *mymtd; + +-__u8 mbx_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-__u16 mbx_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-__u32 mbx_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + struct map_info mbx_map = { +- name: "MBX flash", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: mbx_read8, +- read16: mbx_read16, +- read32: mbx_read32, +- copy_from: mbx_copy_from, +- write8: mbx_write8, +- write16: mbx_write16, +- write32: mbx_write32, +- copy_to: mbx_copy_to ++ .name = "MBX flash", ++ .size = WINDOW_SIZE, ++ .phys = WINDOW_ADDR, ++ .bankwidth = 4, + }; + + int __init init_mbx(void) + { +- printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); ++ mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!mbx_map.map_priv_1) { ++ if (!mbx_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&mbx_map); ++ + mymtd = do_map_probe("jedec_probe", &mbx_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + return 0; + } + +- iounmap((void *)mbx_map.map_priv_1); ++ iounmap((void *)mbx_map.virt); + return -ENXIO; + } + +@@ -130,9 +86,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (mbx_map.map_priv_1) { +- iounmap((void *)mbx_map.map_priv_1); +- mbx_map.map_priv_1 = 0; ++ if (mbx_map.virt) { ++ iounmap((void *)mbx_map.virt); ++ mbx_map.virt = 0; + } + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/mpc1211.c +@@ -0,0 +1,81 @@ ++/* ++ * Flash on MPC-1211 ++ * ++ * $Id$ ++ * ++ * (C) 2002 Interface, Saito.K & Jeanne ++ * ++ * GPL'd ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *flash_mtd; ++static struct mtd_partition *parsed_parts; ++ ++struct map_info mpc1211_flash_map = { ++ .name = "MPC-1211 FLASH", ++ .size = 0x80000, ++ .bankwidth = 1, ++}; ++ ++static struct mtd_partition mpc1211_partitions[] = { ++ { ++ .name = "IPL & ETH-BOOT", ++ .offset = 0x00000000, ++ .size = 0x10000, ++ }, ++ { ++ .name = "Flash FS", ++ .offset = 0x00010000, ++ .size = MTDPART_SIZ_FULL, ++ } ++}; ++ ++static int __init init_mpc1211_maps(void) ++{ ++ int nr_parts; ++ ++ mpc1211_flash_map.phys = 0; ++ mpc1211_flash_map.virt = (void __iomem *)P2SEGADDR(0); ++ ++ simple_map_init(&mpc1211_flash_map); ++ ++ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); ++ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map); ++ if (!flash_mtd) { ++ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n"); ++ return -ENXIO; ++ } ++ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff); ++ flash_mtd->module = THIS_MODULE; ++ ++ parsed_parts = mpc1211_partitions; ++ nr_parts = ARRAY_SIZE(mpc1211_partitions); ++ ++ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); ++ return 0; ++} ++ ++static void __exit cleanup_mpc1211_maps(void) ++{ ++ if (parsed_parts) ++ del_mtd_partitions(flash_mtd); ++ else ++ del_mtd_device(flash_mtd); ++ map_destroy(flash_mtd); ++} ++ ++module_init(init_mpc1211_maps); ++module_exit(cleanup_mpc1211_maps); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Saito.K & Jeanne "); ++MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/mphysmap.c +@@ -0,0 +1,282 @@ ++/* ++ * $Id$ ++ * ++ * Several mappings of NOR chips ++ * ++ * Copyright (c) 2001-2005 Jörn Engel ++ */ ++#include ++#include ++#include ++#include ++ ++ ++#define NO_DEVICES 8 ++struct map_info maps[NO_DEVICES] = { ++#if CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH ++ { ++ .name = CONFIG_MTD_MULTI_PHYSMAP_1_NAME, ++ .phys = CONFIG_MTD_MULTI_PHYSMAP_1_START, ++ .size = CONFIG_MTD_MULTI_PHYSMAP_1_LEN, ++ .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH, ++ }, ++#endif ++#if CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH ++ { ++ .name = CONFIG_MTD_MULTI_PHYSMAP_2_NAME, ++ .phys = CONFIG_MTD_MULTI_PHYSMAP_2_START, ++ .size = CONFIG_MTD_MULTI_PHYSMAP_2_LEN, ++ .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH, ++ }, ++#endif ++#if CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH ++ { ++ .name = CONFIG_MTD_MULTI_PHYSMAP_3_NAME, ++ .phys = CONFIG_MTD_MULTI_PHYSMAP_3_START, ++ .size = CONFIG_MTD_MULTI_PHYSMAP_3_LEN, ++ .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH, ++ }, ++#endif ++#if CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH ++ { ++ .name = CONFIG_MTD_MULTI_PHYSMAP_4_NAME, ++ .phys = CONFIG_MTD_MULTI_PHYSMAP_4_START, ++ .size = CONFIG_MTD_MULTI_PHYSMAP_4_LEN, ++ .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH, ++ }, ++#endif ++ { ++ .name = NULL, ++ }, ++}; ++DECLARE_MUTEX(map_mutex); ++ ++ ++static int map_one(struct map_info *map) ++{ ++ struct mtd_info *mtd; ++ ++ map->virt = ioremap(map->phys, map->size); ++ if (!map->virt) ++ return -EIO; ++ ++ simple_map_init(map); ++ ++ mtd = do_map_probe("cfi_probe", map); ++ if (!mtd) { ++ iounmap(map->virt); ++ return -ENXIO; ++ } ++ ++ map->map_priv_1 = (unsigned long)mtd; ++ mtd->owner = THIS_MODULE; ++ /* TODO: partitioning */ ++ return add_mtd_device(mtd); ++} ++ ++ ++static void unmap_one(struct map_info *map) ++{ ++ struct mtd_info *mtd = (struct mtd_info*)map->map_priv_1; ++ ++ if (map->map_priv_2) ++ kfree(map->name); ++ ++ if (!map->virt) ++ return; ++ ++ BUG_ON(!mtd); ++ BUG_ON(map->map_priv_2 > 1); ++ ++ del_mtd_device(mtd); ++ map_destroy(mtd); ++ iounmap(map->virt); ++ ++ map->map_priv_1 = 0; ++ map->map_priv_2 = 0; ++ map->virt = NULL; ++} ++ ++ ++static struct map_info *next_free_map(void) ++{ ++ int i; ++ for (i=0; ivirt) ++ return map; ++ } ++ return NULL; ++} ++ ++ ++static int add_one_map(const char *name, unsigned long start, ++ unsigned long len, int width) ++{ ++ struct map_info *map = next_free_map(); ++ if (!map) ++ return -ENOSPC; ++ ++ map->name = kmalloc(strlen(name)+1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ strcpy(map->name, name); ++ map->phys = start; ++ map->size = len; ++ map->bankwidth = width; ++ map->map_priv_2 = 1; /* marker to free map->name */ ++ ++ return map_one(map); ++} ++ ++ ++static int parse_ulong(unsigned long *num, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ ++ *num = n; ++ return 0; ++} ++ ++ ++static int parse_uint(unsigned int *num, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ if ((int)n != n) ++ return -EINVAL; ++ ++ *num = n; ++ return 0; ++} ++ ++ ++static int parse_name(char **pname, const char *token, int limit) ++{ ++ size_t len; ++ char *name; ++ ++ len = strlen(token) + 1; ++ if (len > limit) ++ return -ENOSPC; ++ ++ name = kmalloc(len, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ memcpy(name, token, len); ++ ++ *pname = name; ++ return 0; ++} ++ ++ ++static inline void kill_final_newline(char *str) ++{ ++ char *newline = strrchr(str, '\n'); ++ if (newline && !newline[1]) ++ *newline = 0; ++} ++ ++ ++#define parse_err(fmt, args...) do { \ ++ printk(KERN_ERR fmt "\n", ## args); \ ++ return 0; \ ++} while(0) ++ ++/* mphysmap=name,start,len,width */ ++static int mphysmap_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[64+14+14+14], *str = buf; ++ char *token[4]; ++ char *name; ++ unsigned long start; ++ unsigned long len; ++ unsigned int width; ++ int i, ret; ++ ++ if (strnlen(val, sizeof(buf)) >= sizeof(buf)) ++ parse_err("parameter too long"); ++ ++ strcpy(str, val); ++ kill_final_newline(str); ++ ++ for (i=0; i<4; i++) ++ token[i] = strsep(&str, ","); ++ ++ if (str) ++ parse_err("too many arguments"); ++ if (!token[3]) ++ parse_err("not enough arguments"); ++ ++ ret = parse_name(&name, token[0], 64); ++ if (ret == -ENOMEM) ++ parse_err("out of memory"); ++ if (ret == -ENOSPC) ++ parse_err("name too long"); ++ if (ret) ++ parse_err("illegal name: %d", ret); ++ ++ ret = parse_ulong(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address"); ++ ++ ret = parse_ulong(&len, token[2]); ++ if (ret) ++ parse_err("illegal length"); ++ ++ ret = parse_uint(&width, token[3]); ++ if (ret) ++ parse_err("illegal bus width"); ++ ++ down(&map_mutex); ++ ret = add_one_map(name, start, len, width); ++ up(&map_mutex); ++ if (ret == -ENOSPC) ++ parse_err("no free space for new map"); ++ if (ret) ++ parse_err("error while mapping: %d", ret); ++ ++ return 0; ++} ++ ++ ++static int __init mphysmap_init(void) ++{ ++ int i; ++ down(&map_mutex); ++ for (i=0; i"); ++MODULE_DESCRIPTION("Generic configurable extensible MTD map driver"); +--- linux-2.4.21/drivers/mtd/maps/netsc520.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/netsc520.c +@@ -3,7 +3,7 @@ + * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) + * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -50,95 +51,41 @@ + ** recoverable afterwards. + */ + +-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ + { +- name: "NetSc520 boot kernel", +- offset: 0, +- size: 0xc0000 ++ .name = "NetSc520 boot kernel", ++ .offset = 0, ++ .size = 0xc0000 + }, + { +- name: "NetSc520 Low BIOS", +- offset: 0xc0000, +- size: 0x40000 ++ .name = "NetSc520 Low BIOS", ++ .offset = 0xc0000, ++ .size = 0x40000 + }, + { +- name: "NetSc520 file system", +- offset: 0x100000, +- size: 0xe80000 ++ .name = "NetSc520 file system", ++ .offset = 0x100000, ++ .size = 0xe80000 + }, + { +- name: "NetSc520 High BIOS", +- offset: 0xf80000, +- size: 0x80000 ++ .name = "NetSc520 High BIOS", ++ .offset = 0xf80000, ++ .size = 0x80000 + }, + }; + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +-/* +- * If no idea what is going on here. This is taken from the FlashFX stuff. +- */ +-#define ROMCS 1 +- +- + #define WINDOW_SIZE 0x00100000 + #define WINDOW_ADDR 0x00200000 + + static struct map_info netsc520_map = { +- name: "netsc520 Flash Bank", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: netsc520_read8, +- read16: netsc520_read16, +- read32: netsc520_read32, +- copy_from: netsc520_copy_from, +- write8: netsc520_write8, +- write16: netsc520_write16, +- write32: netsc520_write32, +- copy_to: netsc520_copy_to, +- map_priv_2: WINDOW_ADDR ++ .name = "netsc520 Flash Bank", ++ .size = WINDOW_SIZE, ++ .bankwidth = 4, ++ .phys = WINDOW_ADDR, + }; + + #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) +@@ -147,13 +94,16 @@ + + static int __init init_netsc520(void) + { +- printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2); +- netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size); ++ printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); ++ netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); + +- if (!netsc520_map.map_priv_1) { ++ if (!netsc520_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&netsc520_map); ++ + mymtd = do_map_probe("cfi_probe", &netsc520_map); + if(!mymtd) + mymtd = do_map_probe("map_ram", &netsc520_map); +@@ -161,11 +111,11 @@ + mymtd = do_map_probe("map_rom", &netsc520_map); + + if (!mymtd) { +- iounmap((void *)netsc520_map.map_priv_1); ++ iounmap(netsc520_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); + return 0; + } +@@ -176,9 +126,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (netsc520_map.map_priv_1) { +- iounmap((void *)netsc520_map.map_priv_1); +- netsc520_map.map_priv_1 = 0; ++ if (netsc520_map.virt) { ++ iounmap(netsc520_map.virt); ++ netsc520_map.virt = NULL; + } + } + +--- linux-2.4.21/drivers/mtd/maps/nettel.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/nettel.c +@@ -6,7 +6,7 @@ + * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) + * +- * $Id$ ++ * $Id$ + */ + + /****************************************************************************/ +@@ -59,128 +59,72 @@ + + /****************************************************************************/ + +-static __u8 nettel_read8(struct map_info *map, unsigned long ofs) +-{ +- return(readb(map->map_priv_1 + ofs)); +-} +- +-static __u16 nettel_read16(struct map_info *map, unsigned long ofs) +-{ +- return(readw(map->map_priv_1 + ofs)); +-} +- +-static __u32 nettel_read32(struct map_info *map, unsigned long ofs) +-{ +- return(readl(map->map_priv_1 + ofs)); +-} +- +-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + /****************************************************************************/ + + #ifdef CONFIG_MTD_CFI_INTELEXT + static struct map_info nettel_intel_map = { +- name: "SnapGear Intel", +- size: 0, +- buswidth: INTEL_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear Intel", ++ .size = 0, ++ .bankwidth = INTEL_BUSWIDTH, + }; + + static struct mtd_partition nettel_intel_partitions[] = { + { +- name: "SnapGear kernel", +- offset: 0, +- size: 0x000e0000 ++ .name = "SnapGear kernel", ++ .offset = 0, ++ .size = 0x000e0000 + }, + { +- name: "SnapGear filesystem", +- offset: 0x00100000, ++ .name = "SnapGear filesystem", ++ .offset = 0x00100000, + }, + { +- name: "SnapGear config", +- offset: 0x000e0000, +- size: 0x00020000 ++ .name = "SnapGear config", ++ .offset = 0x000e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear Intel", +- offset: 0 ++ .name = "SnapGear Intel", ++ .offset = 0 + }, + { +- name: "SnapGear BIOS Config", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS Config", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + }; + #endif + + static struct map_info nettel_amd_map = { +- name: "SnapGear AMD", +- size: AMD_WINDOW_MAXSIZE, +- buswidth: AMD_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear AMD", ++ .size = AMD_WINDOW_MAXSIZE, ++ .bankwidth = AMD_BUSWIDTH, + }; + + static struct mtd_partition nettel_amd_partitions[] = { + { +- name: "SnapGear BIOS config", +- offset: 0x000e0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS config", ++ .offset = 0x000e0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x000f0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS", ++ .offset = 0x000f0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear AMD", +- offset: 0 ++ .name = "SnapGear AMD", ++ .offset = 0 + }, + { +- name: "SnapGear high BIOS", +- offset: 0x001f0000, +- size: 0x00010000 ++ .name = "SnapGear high BIOS", ++ .offset = 0x001f0000, ++ .size = 0x00010000 + } + }; + +@@ -236,7 +180,7 @@ + if (mtd) { + nettel_erase.mtd = mtd; + nettel_erase.callback = nettel_erasecallback; +- nettel_erase.callback = 0; ++ nettel_erase.callback = NULL; + nettel_erase.addr = 0; + nettel_erase.len = mtd->size; + nettel_erase.priv = (u_long) &wait_q; +@@ -328,18 +272,19 @@ + *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); + __asm__ ("wbinvd"); + +- nettel_amd_map.map_priv_1 = (unsigned long) +- ioremap_nocache(amdaddr, maxsize); +- if (!nettel_amd_map.map_priv_1) { ++ 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"); + return(-EIO); + } ++ simple_map_init(&nettel_amd_map); + + if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { + printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", + amd_mtd->size>>10); + +- amd_mtd->module = THIS_MODULE; ++ amd_mtd->owner = THIS_MODULE; + + /* The high BIOS partition is only present for 2MB units */ + num_amd_partitions = NUM_AMD_PARTITIONS; +@@ -387,8 +332,8 @@ + + /* Destroy useless AMD MTD mapping */ + amd_mtd = NULL; +- iounmap((void *) nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = (unsigned long) NULL; ++ iounmap(nettel_amd_map.virt); ++ nettel_amd_map.virt = NULL; + #else + /* Only AMD flash supported */ + return(-ENXIO); +@@ -411,16 +356,17 @@ + + /* Probe for the the size of the first Intel flash */ + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) +- ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ 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"); + return(-EIO); + } ++ simple_map_init(&nettel_intel_map); + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); +- if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ if (!intel_mtd) { ++ iounmap(nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -441,19 +387,18 @@ + /* Delete the old map and probe again to do both chips */ + map_destroy(intel_mtd); + intel_mtd = NULL; +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap(nettel_intel_map.virt); + + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) +- ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); ++ if (!nettel_intel_map.virt) { + printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); + return(-EIO); + } + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); + if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -468,7 +413,7 @@ + printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", + (intel_mtd->size >> 10)); + +- intel_mtd->module = THIS_MODULE; ++ intel_mtd->owner = THIS_MODULE; + + #ifndef CONFIG_BLK_DEV_INITRD + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); +@@ -523,18 +468,18 @@ + del_mtd_partitions(amd_mtd); + map_destroy(amd_mtd); + } +- if (nettel_amd_map.map_priv_1) { +- iounmap((void *)nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = 0; ++ if (nettel_amd_map.virt) { ++ iounmap(nettel_amd_map.virt); ++ nettel_amd_map.virt = NULL; + } + #ifdef CONFIG_MTD_CFI_INTELEXT + if (intel_mtd) { + del_mtd_partitions(intel_mtd); + map_destroy(intel_mtd); + } +- if (nettel_intel_map.map_priv_1) { +- iounmap((void *)nettel_intel_map.map_priv_1); +- nettel_intel_map.map_priv_1 = 0; ++ if (nettel_intel_map.virt) { ++ iounmap(nettel_intel_map.virt); ++ nettel_intel_map.virt = 0; + } + #endif + } +--- linux-2.4.21/drivers/mtd/maps/ocelot.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/ocelot.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Flash on Momenco Ocelot + */ +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -20,47 +21,23 @@ + #define NVRAM_WINDOW_SIZE 0x00007FF0 + #define NVRAM_BUSWIDTH 1 + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- + static unsigned int cacheflush = 0; + + static struct mtd_info *flash_mtd; + static struct mtd_info *nvram_mtd; + +-__u8 ocelot_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- cacheflush = 1; +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- if (cacheflush) { +- dma_cache_inv(map->map_priv_2, map->size); +- cacheflush = 0; +- } +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++ struct map_info *map = mtd->priv; ++ size_t done = 0; + +-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ + /* If we use memcpy, it does word-wide writes. Even though we told the + GT64120A that it's an 8-bit wide region, word-wide writes don't work. + We end up just writing the first byte of the four to all four bytes. + So we have this loop instead */ ++ *retlen = len; + while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); ++ __raw_writeb(*(unsigned char *) from, map->virt + to); + from++; + to++; + len--; +@@ -70,24 +47,21 @@ + static struct mtd_partition *parsed_parts; + + struct map_info ocelot_flash_map = { +- name: "Ocelot boot flash", +- size: FLASH_WINDOW_SIZE, +- buswidth: FLASH_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from_cache, +- write8: ocelot_write8, ++ .name = "Ocelot boot flash", ++ .size = FLASH_WINDOW_SIZE, ++ .bankwidth = FLASH_BUSWIDTH, ++ .phys = FLASH_WINDOW_ADDR, + }; + + struct map_info ocelot_nvram_map = { +- name: "Ocelot NVRAM", +- size: NVRAM_WINDOW_SIZE, +- buswidth: NVRAM_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from, +- write8: ocelot_write8, +- copy_to: ocelot_copy_to ++ .name = "Ocelot NVRAM", ++ .size = NVRAM_WINDOW_SIZE, ++ .bankwidth = NVRAM_BUSWIDTH, ++ .phys = NVRAM_WINDOW_ADDR, + }; + ++static const char *probes[] = { "RedBoot", NULL }; ++ + static int __init init_ocelot_maps(void) + { + void *pld; +@@ -107,12 +81,13 @@ + iounmap(pld); + + /* Now ioremap the NVRAM space */ +- ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); +- if (!ocelot_nvram_map.map_priv_1) { ++ ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); ++ if (!ocelot_nvram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); + return -EIO; + } +- // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1; ++ ++ simple_map_init(&ocelot_nvram_map); + + /* And do the RAM probe on it to get an MTD device */ + nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); +@@ -120,22 +95,21 @@ + printk("NVRAM probe failed\n"); + goto fail_1; + } +- nvram_mtd->module = THIS_MODULE; ++ nvram_mtd->owner = THIS_MODULE; + nvram_mtd->erasesize = 16; ++ /* Override the write() method */ ++ nvram_mtd->write = ocelot_ram_write; + + /* Now map the flash space */ +- ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); +- if (!ocelot_flash_map.map_priv_1) { ++ ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); ++ if (!ocelot_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); + goto fail_2; + } + /* Now the cached version */ +- ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); ++ ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); + +- if (!ocelot_flash_map.map_priv_2) { +- /* Doesn't matter if it failed. Just use the uncached version */ +- ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1; +- } ++ simple_map_init(&ocelot_flash_map); + + /* Only probe for flash if the write jumper is present */ + if (brd_status & 0x40) { +@@ -155,10 +129,10 @@ + + add_mtd_device(nvram_mtd); + +- flash_mtd->module = THIS_MODULE; +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); ++ flash_mtd->owner = THIS_MODULE; ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); + +- if (nr_parts) ++ if (nr_parts > 0) + add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + else + add_mtd_device(flash_mtd); +@@ -166,14 +140,13 @@ + return 0; + + fail3: +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 && +- ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + fail_2: + map_destroy(nvram_mtd); + fail_1: +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + return -ENXIO; + } +@@ -182,16 +155,16 @@ + { + del_mtd_device(nvram_mtd); + map_destroy(nvram_mtd); +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + if (parsed_parts) + del_mtd_partitions(flash_mtd); + else + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + } + + module_init(init_ocelot_maps); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ocotea.c +@@ -0,0 +1,154 @@ ++/* ++ * Mapping for Ocotea user flash ++ * ++ * Matt Porter ++ * ++ * Copyright 2002-2004 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *flash; ++ ++static struct map_info ocotea_small_map = { ++ .name = "Ocotea small flash", ++ .size = OCOTEA_SMALL_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct map_info ocotea_large_map = { ++ .name = "Ocotea large flash", ++ .size = OCOTEA_LARGE_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct mtd_partition ocotea_small_partitions[] = { ++ { ++ .name = "pibs", ++ .offset = 0x0, ++ .size = 0x100000, ++ } ++}; ++ ++static struct mtd_partition ocotea_large_partitions[] = { ++ { ++ .name = "fs", ++ .offset = 0, ++ .size = 0x300000, ++ }, ++ { ++ .name = "firmware", ++ .offset = 0x300000, ++ .size = 0x100000, ++ } ++}; ++ ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++ ++int __init init_ocotea(void) ++{ ++ u8 fpga0_reg; ++ u8 *fpga0_adr; ++ unsigned long long small_flash_base, large_flash_base; ++ ++ fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16); ++ if (!fpga0_adr) ++ return -ENOMEM; ++ ++ fpga0_reg = readb((unsigned long)fpga0_adr); ++ iounmap(fpga0_adr); ++ ++ if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) { ++ small_flash_base = OCOTEA_SMALL_FLASH_HIGH; ++ large_flash_base = OCOTEA_LARGE_FLASH_LOW; ++ } ++ else { ++ small_flash_base = OCOTEA_SMALL_FLASH_LOW; ++ large_flash_base = OCOTEA_LARGE_FLASH_HIGH; ++ } ++ ++ ocotea_small_map.phys = small_flash_base; ++ ocotea_small_map.virt = ioremap64(small_flash_base, ++ ocotea_small_map.size); ++ ++ if (!ocotea_small_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ocotea_small_map); ++ ++ flash = do_map_probe("map_rom", &ocotea_small_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ocotea_small_partitions, ++ NB_OF(ocotea_small_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ ocotea_large_map.phys = large_flash_base; ++ ocotea_large_map.virt = ioremap64(large_flash_base, ++ ocotea_large_map.size); ++ ++ if (!ocotea_large_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ocotea_large_map); ++ ++ flash = do_map_probe("cfi_probe", &ocotea_large_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ocotea_large_partitions, ++ NB_OF(ocotea_large_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit cleanup_ocotea(void) ++{ ++ if (flash) { ++ del_mtd_partitions(flash); ++ map_destroy(flash); ++ } ++ ++ if (ocotea_small_map.virt) { ++ iounmap((void *)ocotea_small_map.virt); ++ ocotea_small_map.virt = 0; ++ } ++ ++ if (ocotea_large_map.virt) { ++ iounmap((void *)ocotea_large_map.virt); ++ ocotea_large_map.virt = 0; ++ } ++} ++ ++module_init(init_ocotea); ++module_exit(cleanup_ocotea); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matt Porter "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards"); +--- linux-2.4.21/drivers/mtd/maps/octagon-5066.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/octagon-5066.c +@@ -1,4 +1,4 @@ +-// $Id$ ++// $Id$ + /* ###################################################################### + + Octagon 5066 MTD Driver. +@@ -31,6 +31,7 @@ + #include + + #include ++#include + + #define WINDOW_START 0xe8000 + #define WINDOW_LENGTH 0x8000 +@@ -40,7 +41,7 @@ + + static volatile char page_n_dev = 0; + static unsigned long iomapadr; +-static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED; ++static DEFINE_SPINLOCK(oct5066_spin); + + /* + * We use map_priv_1 to identify which device we are. +@@ -61,32 +62,12 @@ + } + + +-static __u8 oct5066_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- spin_lock(&oct5066_spin); +- oct5066_page(map, ofs); +- ret = readb(iomapadr + (ofs & WINDOW_MASK)); +- spin_unlock(&oct5066_spin); +- return ret; +-} +- +-static __u16 oct5066_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- spin_lock(&oct5066_spin); +- oct5066_page(map, ofs); +- ret = readw(iomapadr + (ofs & WINDOW_MASK)); +- spin_unlock(&oct5066_spin); +- return ret; +-} +- +-static __u32 oct5066_read32(struct map_info *map, unsigned long ofs) ++static map_word oct5066_read8(struct map_info *map, unsigned long ofs) + { +- __u32 ret; ++ map_word ret; + spin_lock(&oct5066_spin); + oct5066_page(map, ofs); +- ret = readl(iomapadr + (ofs & WINDOW_MASK)); ++ ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&oct5066_spin); + return ret; + } +@@ -108,27 +89,11 @@ + } + } + +-static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- spin_lock(&oct5066_spin); +- oct5066_page(map, adr); +- writeb(d, iomapadr + (adr & WINDOW_MASK)); +- spin_unlock(&oct5066_spin); +-} +- +-static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- spin_lock(&oct5066_spin); +- oct5066_page(map, adr); +- writew(d, iomapadr + (adr & WINDOW_MASK)); +- spin_unlock(&oct5066_spin); +-} +- +-static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr) ++static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr) + { + spin_lock(&oct5066_spin); + oct5066_page(map, adr); +- writel(d, iomapadr + (adr & WINDOW_MASK)); ++ writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&oct5066_spin); + } + +@@ -151,32 +116,26 @@ + + static struct map_info oct5066_map[2] = { + { +- name: "Octagon 5066 Socket", +- size: 512 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 1<<6 ++ .name = "Octagon 5066 Socket", ++ .phys = NO_XIP, ++ .size = 512 * 1024, ++ .bankwidth = 1, ++ .read = oct5066_read8, ++ .copy_from = oct5066_copy_from, ++ .write = oct5066_write8, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 1<<6 + }, + { +- name: "Octagon 5066 Internal Flash", +- size: 2 * 1024 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 2<<6 ++ .name = "Octagon 5066 Internal Flash", ++ .phys = NO_XIP, ++ .size = 2 * 1024 * 1024, ++ .bankwidth = 1, ++ .read = oct5066_read8, ++ .copy_from = oct5066_copy_from, ++ .write = oct5066_write8, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 2<<6 + } + }; + +@@ -262,7 +221,7 @@ + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); + if (oct5066_mtd[i]) { +- oct5066_mtd[i]->module = THIS_MODULE; ++ oct5066_mtd[i]->owner = THIS_MODULE; + add_mtd_device(oct5066_mtd[i]); + } + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/omap-toto-flash.c +@@ -0,0 +1,137 @@ ++/* ++ * NOR Flash memory access on TI Toto board ++ * ++ * jzhang@ti.com (C) 2003 Texas Instruments. ++ * ++ * (C) 2002 MontVista Software, Inc. ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#ifndef CONFIG_ARCH_OMAP ++#error This is for OMAP architecture only ++#endif ++ ++//these lines need be moved to a hardware header file ++#define OMAP_TOTO_FLASH_BASE 0xd8000000 ++#define OMAP_TOTO_FLASH_SIZE 0x80000 ++ ++static struct map_info omap_toto_map_flash = { ++ .name = "OMAP Toto flash", ++ .bankwidth = 2, ++ .virt = (void __iomem *)OMAP_TOTO_FLASH_BASE, ++}; ++ ++ ++static struct mtd_partition toto_flash_partitions[] = { ++ { ++ .name = "BootLoader", ++ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/ ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ReservedSpace", ++ .size = 0x00030000, ++ .offset = MTDPART_OFS_APPEND, ++ //mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "EnvArea", /* bottom 64KiB for env vars */ ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static struct mtd_partition *parsed_parts; ++ ++static struct mtd_info *flash_mtd; ++ ++static int __init init_flash (void) ++{ ++ ++ struct mtd_partition *parts; ++ int nb_parts = 0; ++ int parsed_nr_parts = 0; ++ const char *part_type; ++ ++ /* ++ * Static partition definition selection ++ */ ++ part_type = "static"; ++ ++ parts = toto_flash_partitions; ++ nb_parts = ARRAY_SIZE(toto_flash_partitions); ++ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE; ++ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE); ++ ++ simple_map_init(&omap_toto_map_flash); ++ /* ++ * Now let's probe for the actual flash. Do it here since ++ * specific machine settings might have been set above. ++ */ ++ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n", ++ omap_toto_map_flash.bankwidth*8); ++ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash); ++ if (!flash_mtd) ++ return -ENXIO; ++ ++ if (parsed_nr_parts > 0) { ++ parts = parsed_parts; ++ nb_parts = parsed_nr_parts; ++ } ++ ++ if (nb_parts == 0) { ++ printk(KERN_NOTICE "OMAP toto flash: no partition info available," ++ "registering whole flash at once\n"); ++ if (add_mtd_device(flash_mtd)){ ++ return -ENXIO; ++ } ++ } else { ++ printk(KERN_NOTICE "Using %s partition definition\n", ++ part_type); ++ return add_mtd_partitions(flash_mtd, parts, nb_parts); ++ } ++ return 0; ++} ++ ++int __init omap_toto_mtd_init(void) ++{ ++ int status; ++ ++ if (status = init_flash()) { ++ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n"); ++ } ++ return status; ++} ++ ++static void __exit omap_toto_mtd_cleanup(void) ++{ ++ if (flash_mtd) { ++ del_mtd_partitions(flash_mtd); ++ map_destroy(flash_mtd); ++ if (parsed_parts) ++ kfree(parsed_parts); ++ } ++} ++ ++module_init(omap_toto_mtd_init); ++module_exit(omap_toto_mtd_cleanup); ++ ++MODULE_AUTHOR("Jian Zhang"); ++MODULE_DESCRIPTION("OMAP Toto board map driver"); ++MODULE_LICENSE("GPL"); +--- linux-2.4.21/drivers/mtd/maps/pci.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/pci.c +@@ -7,7 +7,7 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +- * $Id$ ++ * $Id$ + * + * Generic PCI memory map driver. We support the following boards: + * - Intel IQ80310 ATU. +@@ -33,12 +33,80 @@ + + struct map_pci_info { + struct map_info map; +- void *base; ++ 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)); ++// printk("read8 : %08lx => %02x\n", ofs, val.x[0]); ++ return val; ++} ++ ++#if 0 ++static map_word mtd_pci_read16(struct map_info *_map, unsigned long ofs) ++{ ++ struct map_pci_info *map = (struct map_pci_info *)_map; ++ map_word val; ++ val.x[0] = readw(map->base + map->translate(map, ofs)); ++// printk("read16: %08lx => %04x\n", ofs, val.x[0]); ++ return val; ++} ++#endif ++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)); ++// printk("read32: %08lx => %08x\n", ofs, val.x[0]); ++ 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; ++// printk("write8 : %08lx <= %02x\n", ofs, val.x[0]); ++ writeb(val.x[0], map->base + map->translate(map, ofs)); ++} ++ ++#if 0 ++static void mtd_pci_write16(struct map_info *_map, map_word val, unsigned long ofs) ++{ ++ struct map_pci_info *map = (struct map_pci_info *)_map; ++// printk("write16: %08lx <= %04x\n", ofs, val.x[0]); ++ writew(val.x[0], map->base + map->translate(map, ofs)); ++} ++#endif ++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; ++// printk("write32: %08lx <= %08x\n", ofs, val.x[0]); ++ 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 struct map_info mtd_pci_map = { ++ .phys = NO_XIP, ++ .copy_from = mtd_pci_copyfrom, ++ .copy_to = mtd_pci_copyto, ++}; ++ + /* + * Intel IOP80310 Flash driver + */ +@@ -48,7 +116,10 @@ + { + u32 win_base; + +- map->map.buswidth = 1; ++ 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)); +@@ -72,7 +143,7 @@ + intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map) + { + if (map->base) +- iounmap((void *)map->base); ++ iounmap(map->base); + pci_write_config_dword(dev, 0x44, map->map.map_priv_2); + } + +@@ -98,10 +169,10 @@ + } + + static struct mtd_pci_info intel_iq80310_info = { +- init: intel_iq80310_init, +- exit: intel_iq80310_exit, +- translate: intel_iq80310_translate, +- map_name: "cfi_probe", ++ .init = intel_iq80310_init, ++ .exit = intel_iq80310_exit, ++ .translate = intel_iq80310_translate, ++ .map_name = "cfi_probe", + }; + + /* +@@ -140,14 +211,16 @@ + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); + val |= PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); +- printk("%s: enabling expansion ROM\n", dev->slot_name); ++ printk("%s: enabling expansion ROM\n", pci_name(dev)); + } + } + + if (!len || !base) + return -ENXIO; + +- map->map.buswidth = 4; ++ 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); + +@@ -163,7 +236,7 @@ + u32 val; + + if (map->base) +- iounmap((void *)map->base); ++ iounmap(map->base); + + /* + * We need to undo the PCI BAR2/PCI ROM BAR address alteration. +@@ -181,34 +254,32 @@ + } + + static struct mtd_pci_info intel_dc21285_info = { +- init: intel_dc21285_init, +- exit: intel_dc21285_exit, +- translate: intel_dc21285_translate, +- map_name: "jedec_probe", ++ .init = intel_dc21285_init, ++ .exit = intel_dc21285_exit, ++ .translate = intel_dc21285_translate, ++ .map_name = "jedec_probe", + }; + + /* + * PCI device ID table + */ + +-static struct pci_device_id mtd_pci_ids[] __devinitdata = { ++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_INTEL, ++ .device = 0x530d, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .class = PCI_CLASS_MEMORY_OTHER << 8, ++ .class_mask = 0xffff00, ++ .driver_data = (unsigned long)&intel_iq80310_info, + }, + { +- vendor: PCI_VENDOR_ID_DEC, +- device: PCI_DEVICE_ID_DEC_21285, +- subvendor: 0, /* DC21285 defaults to 0 on reset */ +- subdevice: 0, /* DC21285 defaults to 0 on reset */ +- class: 0, +- class_mask: 0, +- driver_data: (unsigned long)&intel_dc21285_info, ++ .vendor = PCI_VENDOR_ID_DEC, ++ .device = PCI_DEVICE_ID_DEC_21285, ++ .subvendor = 0, /* DC21285 defaults to 0 on reset */ ++ .subdevice = 0, /* DC21285 defaults to 0 on reset */ ++ .driver_data = (unsigned long)&intel_dc21285_info, + }, + { 0, } + }; +@@ -217,74 +288,6 @@ + * Generic code follows. + */ + +-static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +- u8 val = readb(map->base + map->translate(map, ofs)); +-// printk("read8 : %08lx => %02x\n", ofs, val); +- return val; +-} +- +-static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +- u16 val = readw(map->base + map->translate(map, ofs)); +-// printk("read16: %08lx => %04x\n", ofs, val); +- return val; +-} +- +-static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +- u32 val = readl(map->base + map->translate(map, ofs)); +-// printk("read32: %08lx => %08x\n", ofs, val); +- return val; +-} +- +-static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +- memcpy_fromio(to, map->base + map->translate(map, from), len); +-} +- +-static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +-// printk("write8 : %08lx <= %02x\n", ofs, val); +- writeb(val, map->base + map->translate(map, ofs)); +-} +- +-static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +-// printk("write16: %08lx <= %04x\n", ofs, val); +- writew(val, map->base + map->translate(map, ofs)); +-} +- +-static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +-// printk("write32: %08lx <= %08x\n", ofs, val); +- writel(val, map->base + map->translate(map, ofs)); +-} +- +-static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) +-{ +- struct map_pci_info *map = (struct map_pci_info *)_map; +- memcpy_toio(map->base + map->translate(map, to), from, len); +-} +- +-static struct map_info mtd_pci_map = { +- read8: mtd_pci_read8, +- read16: mtd_pci_read16, +- read32: mtd_pci_read32, +- copy_from: mtd_pci_copyfrom, +- write8: mtd_pci_write8, +- write16: mtd_pci_write16, +- write32: mtd_pci_write32, +- copy_to: mtd_pci_copyto, +-}; +- + static int __devinit + mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + { +@@ -307,7 +310,7 @@ + goto release; + + map->map = mtd_pci_map; +- map->map.name = dev->slot_name; ++ map->map.name = pci_name(dev); + map->dev = dev; + map->exit = info->exit; + map->translate = info->translate; +@@ -322,7 +325,7 @@ + if (!mtd) + goto release; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + add_mtd_device(mtd); + + pci_set_drvdata(dev, mtd); +@@ -359,10 +362,10 @@ + } + + static struct pci_driver mtd_pci_driver = { +- name: "MTD PCI", +- probe: mtd_pci_probe, +- remove: __devexit_p(mtd_pci_remove), +- id_table: mtd_pci_ids, ++ .name = "MTD PCI", ++ .probe = mtd_pci_probe, ++ .remove = __devexit_p(mtd_pci_remove), ++ .id_table = mtd_pci_ids, + }; + + static int __init mtd_pci_maps_init(void) +--- linux-2.4.21/drivers/mtd/maps/pcmciamtd.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/pcmciamtd.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * pcmciamtd.c - MTD driver for PCMCIA flash memory cards + * +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -24,6 +25,7 @@ + #include + + #include ++#include + + #ifdef CONFIG_MTD_DEBUG + static int debug = CONFIG_MTD_DEBUG_VERBOSE; +@@ -47,7 +49,7 @@ + + + #define DRIVER_DESC "PCMCIA Flash memory card driver" +-#define DRIVER_VERSION "$Revision$" ++#define DRIVER_VERSION "$Revision$" + + /* Size of the PCMCIA address space: 26 bits = 64 MB */ + #define MAX_PCMCIA_ADDR 0x4000000 +@@ -71,7 +73,7 @@ + /* Module parameters */ + + /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ +-static int buswidth = 2; ++static int bankwidth = 2; + + /* Speed of memory accesses, in ns */ + static int mem_speed; +@@ -91,12 +93,12 @@ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Simon Evans "); + MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_PARM(buswidth, "i"); +-MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)"); ++MODULE_PARM(bankwidth, "i"); ++MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)"); + MODULE_PARM(mem_speed, "i"); + MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); + MODULE_PARM(force_size, "i"); +-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); ++MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)"); + MODULE_PARM(setvpp, "i"); + MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); + MODULE_PARM(vpp, "i"); +@@ -105,16 +107,7 @@ + MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); + + +- +-static inline void cs_error(client_handle_t handle, int func, int ret) +-{ +- error_info_t err = { func, ret }; +- CardServices(ReportError, handle, &err); +-} +- +- + /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +- + static caddr_t remap_window(struct map_info *map, unsigned long to) + { + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; +@@ -132,7 +125,7 @@ + DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x", + dev->offset, mrq.CardOffset); + mrq.Page = 0; +- if( (ret = CardServices(MapMemPage, win, &mrq)) != CS_SUCCESS) { ++ if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) { + cs_error(dev->link.handle, MapMemPage, ret); + return NULL; + } +@@ -142,32 +135,32 @@ + } + + +-static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs) ++static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) + { + caddr_t addr; +- u8 d; ++ map_word d = {{0}}; + + addr = remap_window(map, ofs); + if(!addr) +- return 0; ++ return d; + +- d = readb(addr); +- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d); ++ d.x[0] = readb(addr); ++ DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); + return d; + } + + +-static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs) ++static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) + { + caddr_t addr; +- u16 d; ++ map_word d = {{0}}; + + addr = remap_window(map, ofs); + if(!addr) +- return 0; ++ return d; + +- d = readw(addr); +- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d); ++ d.x[0] = readw(addr); ++ DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); + return d; + } + +@@ -198,26 +191,26 @@ + } + + +-static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr) ++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; + +- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d); +- writeb(d, addr); ++ DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); ++ writeb(d.x[0], addr); + } + + +-static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr) ++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; + +- DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d); +- writew(d, addr); ++ DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); ++ writew(d.x[0], addr); + } + + +@@ -251,30 +244,30 @@ + + #define DEV_REMOVED(x) (!(*(u_int *)x->map_priv_1 & DEV_PRESENT)) + +-static u8 pcmcia_read8(struct map_info *map, unsigned long ofs) ++static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) + { + caddr_t win_base = (caddr_t)map->map_priv_2; +- u8 d; ++ map_word d = {{0}}; + + if(DEV_REMOVED(map)) +- return 0; ++ return d; + +- d = readb(win_base + ofs); +- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d); ++ d.x[0] = readb(win_base + ofs); ++ DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); + return d; + } + + +-static u16 pcmcia_read16(struct map_info *map, unsigned long ofs) ++static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) + { + caddr_t win_base = (caddr_t)map->map_priv_2; +- u16 d; ++ map_word d = {{0}}; + + if(DEV_REMOVED(map)) +- return 0; ++ return d; + +- d = readw(win_base + ofs); +- DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d); ++ d.x[0] = readw(win_base + ofs); ++ DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); + return d; + } + +@@ -339,7 +332,7 @@ + mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; + + DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); +- ret = CardServices(ModifyConfiguration, link->handle, &mod); ++ ret = pcmcia_modify_configuration(link->handle, &mod); + if(ret != CS_SUCCESS) { + cs_error(link->handle, ModifyConfiguration, ret); + } +@@ -351,9 +344,8 @@ + * still open, this will be postponed until it is closed. + */ + +-static void pcmciamtd_release(u_long arg) ++static void pcmciamtd_release(dev_link_t *link) + { +- dev_link_t *link = (dev_link_t *)arg; + struct pcmciamtd_dev *dev = link->priv; + + DEBUG(3, "link = 0x%p", link); +@@ -363,9 +355,9 @@ + iounmap(dev->win_base); + dev->win_base = NULL; + } +- CardServices(ReleaseWindow, link->win); ++ pcmcia_release_window(link->win); + } +- CardServices(ReleaseConfiguration, link->handle); ++ pcmcia_release_configuration(link->handle); + link->state &= ~DEV_CONFIG; + } + +@@ -383,14 +375,14 @@ + tuple.TupleOffset = 0; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + +- rc = CardServices(GetFirstTuple, link->handle, &tuple); ++ rc = pcmcia_get_first_tuple(link->handle, &tuple); + while(rc == CS_SUCCESS) { +- rc = CardServices(GetTupleData, link->handle, &tuple); ++ rc = pcmcia_get_tuple_data(link->handle, &tuple); + if(rc != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, rc); + break; + } +- rc = CardServices(ParseTuple, link->handle, &tuple, &parse); ++ rc = pcmcia_parse_tuple(link->handle, &tuple, &parse); + if(rc != CS_SUCCESS) { + cs_error(link->handle, ParseTuple, rc); + break; +@@ -447,9 +439,9 @@ + case CISTPL_DEVICE_GEO: { + cistpl_device_geo_t *t = &parse.device_geo; + int i; +- dev->pcmcia_map.buswidth = t->geo[0].buswidth; ++ dev->pcmcia_map.bankwidth = t->geo[0].buswidth; + for(i = 0; i < t->ngeo; i++) { +- DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth); ++ DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); + DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); + DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); +@@ -463,22 +455,22 @@ + DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); + } + +- rc = CardServices(GetNextTuple, link->handle, &tuple, &parse); ++ rc = pcmcia_get_next_tuple(link->handle, &tuple); + } + if(!dev->pcmcia_map.size) + dev->pcmcia_map.size = MAX_PCMCIA_ADDR; + +- if(!dev->pcmcia_map.buswidth) +- dev->pcmcia_map.buswidth = 2; ++ if(!dev->pcmcia_map.bankwidth) ++ dev->pcmcia_map.bankwidth = 2; + + if(force_size) { + dev->pcmcia_map.size = force_size << 20; + DEBUG(2, "size forced to %dM", force_size); + } + +- if(buswidth) { +- dev->pcmcia_map.buswidth = buswidth; +- DEBUG(2, "buswidth forced to %d", buswidth); ++ if(bankwidth) { ++ dev->pcmcia_map.bankwidth = bankwidth; ++ DEBUG(2, "bankwidth forced to %d", bankwidth); + } + + dev->pcmcia_map.name = dev->mtd_name; +@@ -488,7 +480,7 @@ + } + + DEBUG(1, "Device: Size: %lu Width:%d Name: %s", +- dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name); ++ dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); + } + + +@@ -497,8 +489,8 @@ + * MTD device available to the system. + */ + +-#define CS_CHECK(fn, args...) \ +-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed ++#define CS_CHECK(fn, ret) \ ++do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + + static void pcmciamtd_config(dev_link_t *link) + { +@@ -520,7 +512,7 @@ + link->state |= DEV_CONFIG; + + DEBUG(2, "Validating CIS"); +- ret = CardServices(ValidateCIS, link->handle, &cisinfo); ++ ret = pcmcia_validate_cis(link->handle, &cisinfo); + if(ret != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, ret); + } else { +@@ -529,21 +521,25 @@ + + card_settings(dev, link, &new_name); + +- dev->pcmcia_map.read8 = pcmcia_read8_remap; +- dev->pcmcia_map.read16 = pcmcia_read16_remap; ++ dev->pcmcia_map.phys = NO_XIP; + dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; +- dev->pcmcia_map.write8 = pcmcia_write8_remap; +- dev->pcmcia_map.write16 = pcmcia_write16_remap; + dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; ++ if (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 upto the maximum +- that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the ++ that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the + whole card - otherwise we try smaller windows until we succeed */ + + req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; +- req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; ++ req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + req.Base = 0; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; +@@ -552,15 +548,14 @@ + + do { + int ret; +- DEBUG(2, "requesting window with size = %dKB memspeed = %d", ++ DEBUG(2, "requesting window with size = %dKiB memspeed = %d", + req.Size >> 10, req.AccessSpeed); +- link->win = (window_handle_t)link->handle; +- ret = CardServices(RequestWindow, &link->win, &req); ++ ret = pcmcia_request_window(&link->handle, &req, &link->win); + DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); + if(ret) { + req.Size >>= 1; + } else { +- DEBUG(2, "Got window of size %dKB", req.Size >> 10); ++ DEBUG(2, "Got window of size %dKiB", req.Size >> 10); + dev->win_size = req.Size; + break; + } +@@ -570,19 +565,19 @@ + + if(!dev->win_size) { + err("Cant allocate memory window"); +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + return; + } +- DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); ++ DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); + + /* Get write protect status */ +- CS_CHECK(GetStatus, link->handle, &status); ++ CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status)); + DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", + status.CardState, (unsigned long)link->win); + dev->win_base = ioremap(req.Base, req.Size); + if(!dev->win_base) { + err("ioremap(%lu, %u) failed", req.Base, req.Size); +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + return; + } + DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", +@@ -593,7 +588,7 @@ + dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; + + DEBUG(2, "Getting configuration"); +- CS_CHECK(GetConfigurationInfo, link->handle, &t); ++ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t)); + DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); + dev->vpp = (vpp) ? vpp : t.Vpp1; + link->conf.Attributes = 0; +@@ -615,7 +610,7 @@ + link->conf.ConfigIndex = 0; + link->conf.Present = t.Present; + DEBUG(2, "Setting Configuration"); +- ret = CardServices(RequestConfiguration, link->handle, &link->conf); ++ ret = pcmcia_request_configuration(link->handle, &link->conf); + if(ret != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, ret); + } +@@ -637,26 +632,26 @@ + + if(!mtd) { + DEBUG(1, "Cant find an MTD"); +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + return; + } + + dev->mtd_info = mtd; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + + if(new_name) { + int size = 0; + char unit = ' '; + /* Since we are using a default name, make it better by adding in the + size */ +- if(mtd->size < 1048576) { /* <1MB in size, show size in K */ ++ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ + size = mtd->size >> 10; + unit = 'K'; + } else { + size = mtd->size >> 20; + unit = 'M'; + } +- snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card"); ++ snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card"); + } + + /* If the memory found is fits completely into the mapped PCMCIA window, +@@ -665,11 +660,14 @@ + DEBUG(1, "Using non remapping memory functions"); + dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state); + dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; +- dev->pcmcia_map.read8 = pcmcia_read8; +- dev->pcmcia_map.read16 = pcmcia_read16; ++ 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.write8 = pcmcia_write8; +- dev->pcmcia_map.write16 = pcmcia_write16; + dev->pcmcia_map.copy_to = pcmcia_copy_to; + } + +@@ -677,7 +675,7 @@ + map_destroy(mtd); + dev->mtd_info = NULL; + err("Couldnt register MTD device"); +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + return; + } + snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); +@@ -689,7 +687,7 @@ + cs_failed: + cs_error(link->handle, last_fn, last_ret); + err("CS Error, exiting"); +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + return; + } + +@@ -716,7 +714,7 @@ + del_mtd_device(dev->mtd_info); + info("mtd%d: Removed", dev->mtd_info->index); + } +- mod_timer(&link->release, jiffies + HZ/20); ++ pcmciamtd_release(link); + } + break; + case CS_EVENT_CARD_INSERTION: +@@ -757,16 +755,14 @@ + { + DEBUG(3, "link=0x%p", link); + +- del_timer(&link->release); +- + if(link->state & DEV_CONFIG) { +- pcmciamtd_release((u_long)link); ++ pcmciamtd_release(link); + } + + if (link->handle) { + int ret; + DEBUG(2, "Deregistering with card services"); +- ret = CardServices(DeregisterClient, link->handle); ++ ret = pcmcia_deregister_client(link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } +@@ -796,10 +792,6 @@ + link = &dev->link; + link->priv = dev; + +- init_timer(&link->release); +- link->release.function = &pcmciamtd_release; +- link->release.data = (u_long)link; +- + link->conf.Attributes = 0; + link->conf.IntType = INT_MEMORY; + +@@ -817,7 +809,7 @@ + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + DEBUG(2, "Calling RegisterClient"); +- ret = CardServices(RegisterClient, &link->handle, &client_reg); ++ ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + pcmciamtd_detach(link); +@@ -828,20 +820,23 @@ + } + + ++static struct pcmcia_driver pcmciamtd_driver = { ++ .drv = { ++ .name = "pcmciamtd" ++ }, ++ .attach = pcmciamtd_attach, ++ .detach = pcmciamtd_detach, ++ .owner = THIS_MODULE ++}; ++ ++ + static int __init init_pcmciamtd(void) + { +- servinfo_t serv; +- + info(DRIVER_DESC " " DRIVER_VERSION); +- CardServices(GetCardServicesInfo, &serv); +- if (serv.Revision != CS_RELEASE_CODE) { +- err("Card Services release does not match!"); +- return -1; +- } + +- if(buswidth && buswidth != 1 && buswidth != 2) { +- info("bad buswidth (%d), using default", buswidth); +- buswidth = 2; ++ if(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); +@@ -851,15 +846,14 @@ + info("bad mem_type (%d), using default", mem_type); + mem_type = 0; + } +- register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); +- return 0; ++ return pcmcia_register_driver(&pcmciamtd_driver); + } + + + static void __exit exit_pcmciamtd(void) + { + DEBUG(1, DRIVER_DESC " unloading"); +- unregister_pccard_driver(&dev_info); ++ pcmcia_unregister_driver(&pcmciamtd_driver); + + while(dev_list) { + dev_link_t *link = dev_list; +--- linux-2.4.21/drivers/mtd/maps/physmap.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/physmap.c +@@ -1,179 +1,119 @@ + /* +- * $Id$ ++ * $Id$ + * + * Normal mappings of chips in physical memory ++ * ++ * Copyright (C) 2003 MontaVista Software Inc. ++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net ++ * ++ * 031022 - [jsun] add run-time configure and partition setup + */ + + #include + #include + #include ++#include ++#include + #include + #include + #include + #include +- +-#ifdef CONFIG_MTD_PARTITIONS + #include +-#endif +- +-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START +-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN +-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH + + static struct mtd_info *mymtd; + +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + struct map_info physmap_map = { +- name: "Physically mapped flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to ++ .name = "phys_mapped_flash", ++ .phys = CONFIG_MTD_PHYSMAP_START, ++ .size = CONFIG_MTD_PHYSMAP_LEN, ++ .bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH, + }; + + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-static struct mtd_partition *mtd_parts = 0; +-static int mtd_parts_nb = 0; +-#else +-static struct mtd_partition physmap_partitions[] = { +-/* Put your own partition definitions here */ +-#if 0 +- { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- } +-#endif +-}; ++static struct mtd_partition *mtd_parts; ++static int mtd_parts_nb; + +-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) ++static int num_physmap_partitions; ++static struct mtd_partition *physmap_partitions; + +-#endif +-#endif ++static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + +-int __init init_physmap(void) ++void physmap_set_partitions(struct mtd_partition *parts, int num_parts) + { +- static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; ++ physmap_partitions=parts; ++ num_physmap_partitions=num_parts; ++} ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static int __init init_physmap(void) ++{ ++ static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; + const char **type; + +- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); +- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); ++ physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size); + +- if (!physmap_map.map_priv_1) { ++ if (!physmap_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + +- mymtd = 0; ++ simple_map_init(&physmap_map); ++ ++ mymtd = NULL; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &physmap_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +- add_mtd_device(mymtd); + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, +- "phys"); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, ++ &mtd_parts, 0); ++ + if (mtd_parts_nb > 0) + { +- printk(KERN_NOTICE +- "Using command line partition definition\n"); + add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); ++ return 0; + } +-#else +- if (NUM_PARTITIONS != 0) ++ ++ if (num_physmap_partitions != 0) + { + printk(KERN_NOTICE + "Using physmap partition definition\n"); +- add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); ++ add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); ++ return 0; + } + + #endif +-#endif ++ add_mtd_device(mymtd); ++ + return 0; + } + +- iounmap((void *)physmap_map.map_priv_1); ++ iounmap(physmap_map.virt); + return -ENXIO; + } + + static void __exit cleanup_physmap(void) + { +- if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (mtd_parts_nb) { ++ del_mtd_partitions(mymtd); ++ kfree(mtd_parts); ++ } else if (num_physmap_partitions) { ++ del_mtd_partitions(mymtd); ++ } else { + del_mtd_device(mymtd); +- map_destroy(mymtd); +- } +- if (physmap_map.map_priv_1) { +- iounmap((void *)physmap_map.map_priv_1); +- physmap_map.map_priv_1 = 0; + } ++#else ++ del_mtd_device(mymtd); ++#endif ++ map_destroy(mymtd); ++ ++ iounmap(physmap_map.virt); ++ physmap_map.virt = NULL; + } + + module_init(init_physmap); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/plat-ram.c +@@ -0,0 +1,286 @@ ++/* drivers/mtd/maps/plat-ram.c ++ * ++ * (c) 2004-2005 Simtec Electronics ++ * http://www.simtec.co.uk/products/SWLINUX/ ++ * Ben Dooks ++ * ++ * Generic platfrom device based RAM map ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* private structure for each mtd platform ram device created */ ++ ++struct platram_info { ++ struct device *dev; ++ struct mtd_info *mtd; ++ struct map_info map; ++ struct mtd_partition *partitions; ++ 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 device *dev) ++{ ++ return (struct platram_info *)dev_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 device *dev) ++{ ++ struct platram_info *info = to_platram_info(dev); ++ ++ dev_set_drvdata(dev, NULL); ++ ++ dev_dbg(dev, "removing device\n"); ++ ++ if (info == NULL) ++ return 0; ++ ++ if (info->mtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (info->partitions) { ++ del_mtd_partitions(info->mtd); ++ kfree(info->partitions); ++ } ++#endif ++ del_mtd_device(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 device *dev) ++{ ++ struct platform_device *pd = to_platform_device(dev); ++ struct platdata_mtd_ram *pdata; ++ struct platram_info *info; ++ struct resource *res; ++ int err = 0; ++ ++ dev_dbg(dev, "probe entered\n"); ++ ++ if (dev->platform_data == NULL) { ++ dev_err(dev, "no platform data supplied\n"); ++ err = -ENOENT; ++ goto exit_error; ++ } ++ ++ pdata = dev->platform_data; ++ ++ info = kmalloc(sizeof(*info), GFP_KERNEL); ++ if (info == NULL) { ++ dev_err(dev, "no memory for flash info\n"); ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ memzero(info, sizeof(*info)); ++ dev_set_drvdata(dev, info); ++ ++ info->dev = dev; ++ info->pdata = pdata; ++ ++ /* get the resource for the memory mapping */ ++ ++ res = platform_get_resource(pd, IORESOURCE_MEM, 0); ++ ++ if (res == NULL) { ++ dev_err(dev, "no memory resource specified\n"); ++ err = -ENOENT; ++ goto exit_free; ++ } ++ ++ dev_dbg(dev, "got platform resource %p (0x%lx)\n", res, res->start); ++ ++ /* setup map parameters */ ++ ++ info->map.phys = res->start; ++ info->map.size = (res->end - res->start) + 1; ++ info->map.name = pdata->mapname != NULL ? pdata->mapname : pd->name; ++ info->map.bankwidth = pdata->bankwidth; ++ ++ /* register our usage of the memory area */ ++ ++ info->area = request_mem_region(res->start, info->map.size, pd->name); ++ if (info->area == NULL) { ++ dev_err(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(dev, "virt %p, %d bytes\n", info->map.virt, info->map.size); ++ ++ if (info->map.virt == NULL) { ++ dev_err(dev, "failed to ioremap() region\n"); ++ err = -EIO; ++ goto exit_free; ++ } ++ ++ { ++ unsigned int *p = (unsigned int *)info->map.virt; ++ printk("%08x %08x %08x %08x\n", ++ readl(p), readl(p+1), readl(p+2), readl(p+3)); ++ } ++ ++ simple_map_init(&info->map); ++ ++ dev_dbg(dev, "initialised map, probing for mtd\n"); ++ ++ /* probe for the right mtd map driver */ ++ ++ info->mtd = do_map_probe("map_ram" , &info->map); ++ if (info->mtd == NULL) { ++ dev_err(dev, "failed to probe for map_ram\n"); ++ err = -ENOMEM; ++ goto exit_free; ++ } ++ ++ info->mtd->owner = THIS_MODULE; ++ ++ platram_setrw(info, PLATRAM_RW); ++ ++ /* check to see if there are any available partitions, or wether ++ * to add this device whole */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ if (pdata->nr_partitions > 0) { ++ const char **probes = { NULL }; ++ ++ if (pdata->probes) ++ probes = (const char **)pdata->probes; ++ ++ err = parse_mtd_partitions(info->mtd, probes, ++ &info->partitions, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->partitions, ++ err); ++ } ++ } ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ if (add_mtd_device(info->mtd)) { ++ dev_err(dev, "add_mtd_device() failed\n"); ++ err = -ENOMEM; ++ } ++ ++ dev_info(dev, "registered mtd device\n"); ++ return err; ++ ++ exit_free: ++ platram_remove(dev); ++ exit_error: ++ return err; ++} ++ ++/* device driver info */ ++ ++static struct device_driver platram_driver = { ++ .name = "mtd-ram", ++ .bus = &platform_bus_type, ++ .probe = platram_probe, ++ .remove = platram_remove, ++}; ++ ++/* module init/exit */ ++ ++static int __init platram_init(void) ++{ ++ printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n"); ++ return driver_register(&platram_driver); ++} ++ ++static void __exit platram_exit(void) ++{ ++ driver_unregister(&platram_driver); ++} ++ ++module_init(platram_init); ++module_exit(platram_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ben Dooks "); ++MODULE_DESCRIPTION("MTD platform RAM map driver"); +--- linux-2.4.21/drivers/mtd/maps/pnc2000.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/pnc2000.c +@@ -5,12 +5,13 @@ + * + * This code is GPL + * +- * $Id$ ++ * $Id$ + */ + + #include + #include + #include ++#include + + #include + #include +@@ -24,58 +25,13 @@ + * MAP DRIVER STUFF + */ + +-__u8 pnc_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(WINDOW_ADDR + ofs); +-} +- +-__u16 pnc_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(WINDOW_ADDR + ofs); +-} +- +-__u32 pnc_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(WINDOW_ADDR + ofs); +-} +- +-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(WINDOW_ADDR + from), len); +-} +- +-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(WINDOW_ADDR + to), from, len); +-} + +-struct map_info pnc_map = { +- name: "PNC-2000", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: pnc_read8, +- read16: pnc_read16, +- read32: pnc_read32, +- copy_from: pnc_copy_from, +- write8: pnc_write8, +- write16: pnc_write16, +- write32: pnc_write32, +- copy_to: pnc_copy_to ++static struct map_info pnc_map = { ++ .name = "PNC-2000", ++ .size = WINDOW_SIZE, ++ .bankwidth = 4, ++ .phys = 0xFFFFFFFF, ++ .virt = (void __iomem *)WINDOW_ADDR, + }; + + +@@ -84,19 +40,19 @@ + */ + static struct mtd_partition pnc_partitions[3] = { + { +- name: "PNC-2000 boot firmware", +- size: 0x20000, +- offset: 0 ++ .name = "PNC-2000 boot firmware", ++ .size = 0x20000, ++ .offset = 0 + }, + { +- name: "PNC-2000 kernel", +- size: 0x1a0000, +- offset: 0x20000 ++ .name = "PNC-2000 kernel", ++ .size = 0x1a0000, ++ .offset = 0x20000 + }, + { +- name: "PNC-2000 filesystem", +- size: 0x240000, +- offset: 0x1c0000 ++ .name = "PNC-2000 filesystem", ++ .size = 0x240000, ++ .offset = 0x1c0000 + } + }; + +@@ -106,13 +62,15 @@ + */ + static struct mtd_info *mymtd; + +-int __init init_pnc2000(void) ++static int __init init_pnc2000(void) + { + printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + ++ simple_map_init(&pnc_map); ++ + mymtd = do_map_probe("cfi_probe", &pnc_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + return add_mtd_partitions(mymtd, pnc_partitions, 3); + } + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ramses.c +@@ -0,0 +1,124 @@ ++/* ++ * Map driver for the Ramses developer platform. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define WINDOW_ADDR 0 ++#define WINDOW_SIZE 32*1024*1024 ++ ++static struct map_info ramses_map_flash = { ++ .name = "Flash", ++ .phys = WINDOW_ADDR, ++ .size = WINDOW_SIZE, ++ .bankwidth = 4, ++}; ++ ++static struct mtd_partition ramses_flash_partitions[] = { ++ { ++ name: "Bootloader", ++ size: 0x00040000, ++ offset: 0, ++ },{ ++ name: "Kernel", ++ size: 0x00100000, ++ offset: 0x00040000, ++ },{ ++ name: "Filesystem", ++ size: MTDPART_SIZ_FULL, ++ offset: 0x00140000 ++ } ++}; ++ ++static struct mtd_partition *parsed_parts; ++ ++static struct mtd_info *flash_mtd; ++static int __init init_ramses(void) ++{ ++ struct mtd_partition *parts; ++ int nb_parts = 0; ++ int parsed_nr_parts = 0; ++ const char *part_type; ++ ++ /* ++ * Static partition definition selection ++ */ ++ part_type = "static"; ++ parts = ramses_flash_partitions; ++ nb_parts = ARRAY_SIZE(ramses_flash_partitions); ++ ++ simple_map_init(&ramses_map_flash); ++ ++ printk( "Probing flash at physical address 0x%08x (%d-bit buswidth)\n", ++ WINDOW_ADDR, ramses_map_flash.bankwidth * 8 ); ++#ifdef CONFIG_ARCH_RAMSES ++ FLASH_WRITE_PROTECT_DISABLE(); ++#endif ++ ++ ++ ramses_map_flash.virt = __ioremap(WINDOW_ADDR, WINDOW_SIZE, 0); ++ if (!ramses_map_flash.virt) { ++ printk("Failed to ioremap\n"); ++ return -EIO; ++ } ++ flash_mtd = do_map_probe("cfi_probe", &ramses_map_flash); ++ ++ if (!flash_mtd) { ++ iounmap((void *)ramses_map_flash.virt); ++ return -ENXIO; ++ } ++ ++ if (parsed_nr_parts > 0) { ++ parts = parsed_parts; ++ nb_parts = parsed_nr_parts; ++ } ++ ++ if (nb_parts == 0) { ++ printk(KERN_NOTICE "Ramses flash: no partition info available," ++ "registering whole flash at once\n"); ++ if (add_mtd_device(flash_mtd)){ ++ return -ENXIO; ++ } ++ } else { ++ printk(KERN_NOTICE "Using %s partition definition\n", ++ part_type); ++ return add_mtd_partitions(flash_mtd, parts, nb_parts); ++ } ++ return 0; ++} ++ ++static void __exit cleanup_ramses(void) ++{ ++ if (flash_mtd) { ++ del_mtd_partitions(flash_mtd); ++ map_destroy(flash_mtd); ++ if (parsed_parts) ++ kfree(parsed_parts); ++ } ++ if (ramses_map_flash.virt) ++ iounmap(ramses_map_flash.virt); ++#ifdef CONFIG_ARCH_RAMSES ++ FLASH_WRITE_PROTECT_ENABLE(); ++#endif ++ return; ++} ++ ++module_init(init_ramses); ++module_exit(cleanup_ramses); +--- linux-2.4.21/drivers/mtd/maps/redwood.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/redwood.c +@@ -1,38 +1,23 @@ + /* +- * $Id: +- * +- * redwood.c - mapper for IBM Redwood-4/5 board. +- * +- * Copyright 2001 MontaVista Softare 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. ++ * $Id$ + * +- * 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. ++ * drivers/mtd/maps/redwood.c + * +- * 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. ++ * FLASH map for the IBM Redwood 4/5/6 boards. + * +- * History: 12/17/2001 - Armin +- * migrated to use do_map_probe ++ * Author: MontaVista Software, Inc. + * ++ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. + */ + ++#include + #include + #include + #include ++#include + + #include + #include +@@ -40,96 +25,102 @@ + + #include + ++#if !defined (CONFIG_REDWOOD_6) ++ + #define WINDOW_ADDR 0xffc00000 + #define WINDOW_SIZE 0x00400000 + +-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(map->map_priv_1 + ofs); +-} +- +-void redwood_flash_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x10000 ++#define RW_PART1_OF RW_PART0_SZ ++#define RW_PART1_SZ 0x200000 - 0x10000 ++#define RW_PART2_OF 0x200000 ++#define RW_PART2_SZ 0x10000 ++#define RW_PART3_OF 0x210000 ++#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000) ++#define RW_PART4_OF 0x3e0000 ++#define RW_PART4_SZ 0x20000 + +-struct map_info redwood_flash_map = { +- name: "IBM Redwood", +- size: WINDOW_SIZE, +- buswidth: 2, +- read8: redwood_flash_read8, +- read16: redwood_flash_read16, +- read32: redwood_flash_read32, +- copy_from: redwood_flash_copy_from, +- write8: redwood_flash_write8, +- write16: redwood_flash_write16, +- write32: redwood_flash_write32, +- copy_to: redwood_flash_copy_to ++static struct mtd_partition redwood_flash_partitions[] = { ++ { ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood kernel", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS non-volatile storage", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood filesystem", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART4_OF, ++ .size = RW_PART4_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ } + }; + ++#else /* CONFIG_REDWOOD_6 */ ++/* FIXME: the window is bigger - armin */ ++#define WINDOW_ADDR 0xff800000 ++#define WINDOW_SIZE 0x00800000 ++ ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x400000 /* 4 MiB data */ ++#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ ++#define RW_PART1_SZ 0x10000 /* 64K VPD */ ++#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ ++#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000) ++#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ ++#define RW_PART3_SZ 0x20000 + + static struct mtd_partition redwood_flash_partitions[] = { + { +- name: "Redwood OpenBIOS Vital Product Data", +- offset: 0, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ +- }, +- { +- name: "Redwood kernel", +- offset: 0x10000, +- size: 0x200000 - 0x10000 ++ .name = "Redwood filesystem", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ + }, + { +- name: "Redwood OpenBIOS non-volatile storage", +- offset: 0x200000, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { +- name: "Redwood filesystem", +- offset: 0x210000, +- size: 0x200000 - (0x10000 + 0x20000) ++ .name = "Redwood kernel", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ + }, + { +- name: "Redwood OpenBIOS", +- offset: 0x3e0000, +- size: 0x20000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + } + }; ++ ++#endif /* CONFIG_REDWOOD_6 */ ++ ++struct map_info redwood_flash_map = { ++ .name = "IBM Redwood", ++ .size = WINDOW_SIZE, ++ .bankwidth = 2, ++ .phys = WINDOW_ADDR, ++}; ++ ++ + #define NUM_REDWOOD_FLASH_PARTITIONS \ + (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0])) + +@@ -140,18 +131,18 @@ + printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", + WINDOW_SIZE, WINDOW_ADDR); + +- redwood_flash_map.map_priv_1 = +- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ redwood_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!redwood_flash_map.map_priv_1) { ++ if (!redwood_flash_map.virt) { + printk("init_redwood_flash: failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&redwood_flash_map); + + redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map); + + if (redwood_mtd) { +- redwood_mtd->module = THIS_MODULE; ++ redwood_mtd->owner = THIS_MODULE; + return add_mtd_partitions(redwood_mtd, + redwood_flash_partitions, + NUM_REDWOOD_FLASH_PARTITIONS); +@@ -164,10 +155,15 @@ + { + if (redwood_mtd) { + del_mtd_partitions(redwood_mtd); +- iounmap((void *)redwood_flash_map.map_priv_1); ++ /* moved iounmap after map_destroy - armin */ + map_destroy(redwood_mtd); ++ iounmap((void *)redwood_flash_map.virt); + } + } + + module_init(init_redwood_flash); + module_exit(cleanup_redwood_flash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("MontaVista Software "); ++MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); +--- linux-2.4.21/drivers/mtd/maps/rpxlite.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/rpxlite.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Handle mapping of the flash on the RPX Lite and CLLF boards + */ +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -17,80 +18,31 @@ + + static struct mtd_info *mymtd; + +-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +-struct map_info rpxlite_map = { +- name: "RPX", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: rpxlite_read8, +- read16: rpxlite_read16, +- read32: rpxlite_read32, +- copy_from: rpxlite_copy_from, +- write8: rpxlite_write8, +- write16: rpxlite_write16, +- write32: rpxlite_write32, +- copy_to: rpxlite_copy_to ++static struct map_info rpxlite_map = { ++ .name = "RPX", ++ .size = WINDOW_SIZE, ++ .bankwidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_rpxlite(void) + { + printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!rpxlite_map.map_priv_1) { ++ if (!rpxlite_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&rpxlite_map); + mymtd = do_map_probe("cfi_probe", &rpxlite_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)rpxlite_map.map_priv_1); ++ iounmap((void *)rpxlite_map.virt); + return -ENXIO; + } + +@@ -100,9 +52,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (rpxlite_map.map_priv_1) { +- iounmap((void *)rpxlite_map.map_priv_1); +- rpxlite_map.map_priv_1 = 0; ++ if (rpxlite_map.virt) { ++ iounmap((void *)rpxlite_map.virt); ++ rpxlite_map.virt = 0; + } + } + +--- linux-2.4.21/drivers/mtd/maps/sa1100-flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/sa1100-flash.c +@@ -3,7 +3,7 @@ + * + * (C) 2000 Nicolas Pitre + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -11,330 +11,212 @@ + #include + #include + #include ++#include ++#include ++#include + + #include + #include + #include ++#include + + #include ++#include + #include ++#include + ++#include + + #ifndef CONFIG_ARCH_SA1100 + #error This is for SA1100 architecture only + #endif + ++/* ++ * This isnt complete yet, so... ++ */ ++#define CONFIG_MTD_SA1100_STATICMAP 1 + +-#define WINDOW_ADDR 0xe8000000 +- +-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info sa1100_map = { +- name: "SA1100 flash", +- read8: sa1100_read8, +- read16: sa1100_read16, +- read32: sa1100_read32, +- copy_from: sa1100_copy_from, +- write8: sa1100_write8, +- write16: sa1100_write16, +- write32: sa1100_write32, +- copy_to: sa1100_copy_to, +- +- map_priv_1: WINDOW_ADDR, +- map_priv_2: -1, +-}; +- +- ++#ifdef CONFIG_MTD_SA1100_STATICMAP + /* + * Here are partition information for all known SA1100-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * +- * The *_max_flash_size is the maximum possible mapped flash size which +- * is not necessarily the actual flash size. It must be no more than +- * the value specified in the "struct map_desc *_io_desc" mapping +- * definition for the corresponding machine. ++ * Please note: ++ * 1. We no longer support static flash mappings via the machine io_desc ++ * structure. ++ * 2. The flash size given should be the largest flash size that can ++ * be accommodated. ++ * ++ * The MTD layer will detect flash chip aliasing and reduce the size of ++ * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + +-#ifdef CONFIG_SA1100_ADSAGC +-#define ADSAGC_FLASH_SIZE 0x02000000 +-static struct mtd_partition adsagc_partitions[] = { +- { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- } +-}; +-#endif +- + #ifdef CONFIG_SA1100_ADSBITSY +-#define ADSBITSY_FLASH_SIZE 0x02000000 + static struct mtd_partition adsbitsy_partitions[] = { + { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- } +-}; +-#endif +- +-#ifdef CONFIG_SA1100_ADSBITSYPLUS +-#define ADSBITSYPLUS_FLASH_SIZE 0x02000000 +-static struct mtd_partition adsbitsyplus_partitions[] = { +- { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "bootROM", ++ .size = 0x80000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_ASSABET + /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ +-#define ASSABET4_FLASH_SIZE 0x00400000 + static struct mtd_partition assabet4_partitions[] = { + { +- name: "bootloader", +- size: 0x00020000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader", ++ .size = 0x00020000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "bootloader params", +- size: 0x00020000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader params", ++ .size = 0x00020000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + + /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ +-#define ASSABET5_FLASH_SIZE 0x02000000 + static struct mtd_partition assabet5_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + +-#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE + #define assabet_partitions assabet5_partitions + #endif + + #ifdef CONFIG_SA1100_BADGE4 +- + /* +- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) ++ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) ++ * ++ * ++ * ++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) ++ * Eight 4 KiW Parameter Bottom Blocks (64 KiB) ++ * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + */ +-#define BADGE4_FLASH_SIZE 0x00400000 + static struct mtd_partition badge4_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x0000A000 +- }, { +- name: "params", +- offset: MTDPART_OFS_APPEND, +- size: 0x00006000 ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x0000A000 + }, { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0x00100000 ++ .name = "params", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0x00006000 + }, { +- name: "root", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "root", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; +- + #endif + + + #ifdef CONFIG_SA1100_CERF + #ifdef CONFIG_SA1100_CERF_FLASH_32MB +-#define CERF_FLASH_SIZE 0x02000000 +-static struct mtd_partition cerf_partitions[] = { +- { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- }, { +- name: "params", +- size: 0x00040000, +- offset: 0x00040000, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +- name: "rootdisk", +- size: 0x01E80000, +- offset: 0x00180000, +- } +-}; ++# define CERF_FLASH_SIZE 0x02000000 + #elif defined CONFIG_SA1100_CERF_FLASH_16MB +-#define CERF_FLASH_SIZE 0x01000000 ++# define CERF_FLASH_SIZE 0x01000000 ++#elif defined CONFIG_SA1100_CERF_FLASH_8MB ++# define CERF_FLASH_SIZE 0x00800000 ++#else ++# error "Undefined flash size for CERF in sa1100-flash.c" ++#endif ++ + static struct mtd_partition cerf_partitions[] = { + { +- name: "firmware", +- size: 0x00020000, +- offset: 0, ++ .name = "Bootloader", ++ .size = 0x00020000, ++ .offset = 0x00000000, + }, { +- name: "params", +- size: 0x00020000, +- offset: 0x00020000, ++ .name = "Params", ++ .size = 0x00040000, ++ .offset = 0x00020000, + }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00040000, ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00060000, + }, { +- name: "rootdisk", +- size: 0x00EC0000, +- offset: 0x00140000, ++ .name = "Filesystem", ++ .size = CERF_FLASH_SIZE-0x00160000, ++ .offset = 0x00160000, + } + }; +-#elif defined CONFIG_SA1100_CERF_FLASH_8MB +-# error "Unwritten type definition" +-#else +-# error "Undefined memory orientation for CERF in sa1100-flash.c" +-#endif + #endif + + #ifdef CONFIG_SA1100_CONSUS +-#define CONSUS_FLASH_SIZE 0x02000000 + static struct mtd_partition consus_partitions[] = { + { +- name: "Consus boot firmware", +- offset: 0, +- size: 0x00040000, +- mask_flags: MTD_WRITABLE, /* force read-only */ ++ .name = "Consus boot firmware", ++ .offset = 0, ++ .size = 0x00040000, ++ .mask_flags = MTD_WRITABLE, /* force read-only */ + }, { +- name: "Consus kernel", +- offset: 0x00040000, +- size: 0x00100000, +- mask_flags: 0, ++ .name = "Consus kernel", ++ .offset = 0x00040000, ++ .size = 0x00100000, ++ .mask_flags = 0, + }, { +- name: "Consus disk", +- offset: 0x00140000, ++ .name = "Consus disk", ++ .offset = 0x00140000, + /* The rest (up to 16M) for jffs. We could put 0 and + make it find the size automatically, but right now + i have 32 megs. jffs will use all 32 megs if given + the chance, and this leads to horrible problems + when you try to re-flash the image because blob + won't erase the whole partition. */ +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + }, { + /* this disk is a secondary disk, which can be used as + needed, for simplicity, make it the size of the other + consus partition, although realistically it could be + the remainder of the disk (depending on the file + system used) */ +- name: "Consus disk2", +- offset: 0x01000000, +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .name = "Consus disk2", ++ .offset = 0x01000000, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + } + }; + #endif +@@ -344,96 +226,95 @@ + #define FLEXANET_FLASH_SIZE 0x02000000 + static struct mtd_partition flexanet_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "kernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "kernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "altkernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "altkernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "root", +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "root", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "free1", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "free1", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "free2", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "free2", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + }, { +- name: "free3", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "free3", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + } + }; + #endif + + #ifdef CONFIG_SA1100_FREEBIRD +-#define FREEBIRD_FLASH_SIZE 0x02000000 + static struct mtd_partition freebird_partitions[] = { +-#if CONFIG_SA1100_FREEBIRD_NEW ++#ifdef CONFIG_SA1100_FREEBIRD_NEW + { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00040000, ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, + }, { +- name: "params", +- size: 0x00040000, +- offset: 0x000C0000, ++ .name = "params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, + }, { +- name: "initrd", +- size: 0x00100000, +- offset: 0x00100000, ++ .name = "initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, + }, { +- name: "root cramfs", +- size: 0x00300000, +- offset: 0x00200000, ++ .name = "root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, + }, { +- name: "usr cramfs", +- size: 0x00C00000, +- offset: 0x00500000, ++ .name = "usr cramfs", ++ .size = 0x00C00000, ++ .offset = 0x00500000, + }, { +- name: "local", +- size: MTDPART_SIZ_FULL, +- offset: 0x01100000, ++ .name = "local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x01100000, + } + #else + { +- size: 0x00040000, +- offset: 0, ++ .size = 0x00040000, ++ .offset = 0, + }, { +- size: 0x000c0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x000c0000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + #endif + }; +@@ -441,206 +322,237 @@ + + #ifdef CONFIG_SA1100_FRODO + /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +-#define FRODO_FLASH_SIZE 0x02000000 + static struct mtd_partition frodo_partitions[] = + { + { +- name: "Boot Loader", +- size: 0x00040000, +- offset: 0x00000000 ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE + }, { +- name: "Parameter Block", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + }, { +- name: "Linux Kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + }, { +- name: "Ramdisk", +- size: 0x00680000, +- offset: MTDPART_OFS_APPEND ++ .name = "ramdisk", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + }, { +- name: "Flash File System", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND ++ .name = "file system", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSCLIENT +-#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000 + static struct mtd_partition graphicsclient_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSMASTER +-#define GRAPHICSMASTER_FLASH_SIZE 0x02000000 + static struct mtd_partition graphicsmaster_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + +-#ifdef CONFIG_SA1100_H3600 +-#define H3600_FLASH_SIZE 0x02000000 +-static struct mtd_partition h3600_partitions[] = { ++#ifdef CONFIG_SA1100_H3XXX ++static struct mtd_partition h3xxx_partitions[] = { + { +- name: "H3600 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "H3XXX boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "H3600 kernel", +- size: 0x00080000, +- offset: 0x00040000, ++#ifdef CONFIG_MTD_2PARTS_IPAQ ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00040000, ++#else ++ .name = "H3XXX kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, + }, { +- name: "H3600 params", +- size: 0x00040000, +- offset: 0x000C0000, ++ .name = "H3XXX params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "H3600 root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00100000, ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00100000, + #else +- name: "H3600 initrd", +- size: 0x00100000, +- offset: 0x00100000, ++ .name = "H3XXX initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, + }, { +- name: "H3600 root cramfs", +- size: 0x00300000, +- offset: 0x00200000, ++ .name = "H3XXX root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, + }, { +- name: "H3600 usr cramfs", +- size: 0x00800000, +- offset: 0x00500000, ++ .name = "H3XXX usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00500000, + }, { +- name: "H3600 usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d00000, ++ .name = "H3XXX usr local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00d00000, ++#endif + #endif + } + }; + +-static void h3600_set_vpp(struct map_info *map, int vpp) ++static void h3xxx_set_vpp(struct map_info *map, int vpp) + { + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); + } ++#else ++#define h3xxx_set_vpp NULL + #endif + + #ifdef CONFIG_SA1100_HACKKIT +-#define HACKKIT_FLASH_SIZE 0x01000000 + static struct mtd_partition hackkit_partitions[] = { + { +- name: "BLOB", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "config", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND, ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "initrd", +- size: 0x00180000, +- offset: MTDPART_OFS_APPEND, ++ .name = "initrd", ++ .size = 0x00180000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "rootfs", +- size: 0x700000, +- offset: MTDPART_OFS_APPEND, ++ .name = "rootfs", ++ .size = 0x700000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "data", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "data", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_HUW_WEBPANEL +-#define HUW_WEBPANEL_FLASH_SIZE 0x01000000 + static struct mtd_partition huw_webpanel_partitions[] = { + { +- name: "Loader", +- size: 0x00040000, +- offset: 0, ++ .name = "Loader", ++ .size = 0x00040000, ++ .offset = 0, + }, { +- name: "Sector 1", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, ++ .name = "Sector 1", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + ++#ifdef CONFIG_SA1100_JORNADA56X ++static struct mtd_partition jornada56x_partitions[] = { ++ { ++ .name = "bootldr", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "rootfs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static void jornada56x_set_vpp(struct map_info *map, int vpp) ++{ ++ if (vpp) ++ GPSR = GPIO_GPIO26; ++ else ++ GPCR = GPIO_GPIO26; ++ GPDR |= GPIO_GPIO26; ++} ++#else ++#define jornada56x_set_vpp NULL ++#endif ++ + #ifdef CONFIG_SA1100_JORNADA720 +-#define JORNADA720_FLASH_SIZE 0x02000000 + static struct mtd_partition jornada720_partitions[] = { + { +- name: "JORNADA720 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "JORNADA720 boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "JORNADA720 kernel", +- size: 0x000c0000, +- offset: 0x00040000, ++ .name = "JORNADA720 kernel", ++ .size = 0x000c0000, ++ .offset = 0x00040000, + }, { +- name: "JORNADA720 params", +- size: 0x00040000, +- offset: 0x00100000, ++ .name = "JORNADA720 params", ++ .size = 0x00040000, ++ .offset = 0x00100000, + }, { +- name: "JORNADA720 initrd", +- size: 0x00100000, +- offset: 0x00140000, ++ .name = "JORNADA720 initrd", ++ .size = 0x00100000, ++ .offset = 0x00140000, + }, { +- name: "JORNADA720 root cramfs", +- size: 0x00300000, +- offset: 0x00240000, ++ .name = "JORNADA720 root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00240000, + }, { +- name: "JORNADA720 usr cramfs", +- size: 0x00800000, +- offset: 0x00540000, ++ .name = "JORNADA720 usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00540000, + }, { +- name: "JORNADA720 usr local", +- size: 0, /* will expand to the end of the flash */ +- offset: 0x00d00000, ++ .name = "JORNADA720 usr local", ++ .size = 0, /* will expand to the end of the flash */ ++ .offset = 0x00d00000, + } + }; + +@@ -652,540 +564,820 @@ + PPSR &= ~0x80; + PPDR |= 0x80; + } +- +-#endif +- +-#ifdef CONFIG_SA1100_NANOENGINE +-/* nanoEngine has one 28F320B3B Flash part in bank 0: */ +-#define NANOENGINE_FLASH_SIZE 0x00400000 +-static struct mtd_partition nanoengine_partitions[] = { +- { +- name: "nanoEngine boot firmware and parameter table", +- size: 0x00010000, /* 32K */ +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- },{ +- name: "kernel/initrd reserved", +- size: 0x002f0000, +- offset: 0x00010000, +- },{ +- name: "experimental filesystem allocation", +- size: 0x00100000, +- offset: 0x00300000, +- } +-}; ++#else ++#define jornada720_set_vpp NULL + #endif + + #ifdef CONFIG_SA1100_PANGOLIN +-#define PANGOLIN_FLASH_SIZE 0x04000000 + static struct mtd_partition pangolin_partitions[] = { + { +- name: "boot firmware", +- size: 0x00080000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "boot firmware", ++ .size = 0x00080000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, + }, { +- name: "initrd", +- size: 0x00280000, +- offset: 0x00180000, ++ .name = "initrd", ++ .size = 0x00280000, ++ .offset = 0x00180000, + }, { +- name: "initrd-test", +- size: 0x03C00000, +- offset: 0x00400000, ++ .name = "initrd-test", ++ .size = 0x03C00000, ++ .offset = 0x00400000, + } + }; + #endif + + #ifdef CONFIG_SA1100_PT_SYSTEM3 + /* erase size is 0x40000 == 256k partitions have to have this boundary */ +-#define SYSTEM3_FLASH_SIZE 0x01000000 + static struct mtd_partition system3_partitions[] = { + { +- name: "BLOB", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "config", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND, ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "root", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SHANNON +-#define SHANNON_FLASH_SIZE 0x00400000 + static struct mtd_partition shannon_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x20000 ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x20000 + }, + { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0xe0000 ++ .name = "kernel", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0xe0000 + }, + { +- name: "initrd", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "initrd", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; + + #endif + + #ifdef CONFIG_SA1100_SHERMAN +-#define SHERMAN_FLASH_SIZE 0x02000000 + static struct mtd_partition sherman_partitions[] = { + { +- size: 0x50000, +- offset: 0, ++ .size = 0x50000, ++ .offset = 0, + }, { +- size: 0x70000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x70000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x600000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x600000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0xA0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0xA0000, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SIMPAD +-#define SIMPAD_FLASH_SIZE 0x02000000 + static struct mtd_partition simpad_partitions[] = { + { +- name: "SIMpad boot firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "SIMpad kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +-#ifdef CONFIG_JFFS2_FS +- name: "SIMpad root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00180000, +-#else +- name: "SIMpad initrd", +- size: 0x00300000, +- offset: 0x00180000, ++ .name = "SIMpad boot firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "SIMpad root cramfs", +- size: 0x00300000, +- offset: 0x00480000, ++ .name = "SIMpad kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- name: "SIMpad usr cramfs", +- size: 0x005c0000, +- offset: 0x00780000, ++#ifdef CONFIG_ROOT_CRAMFS ++ .name = "SIMpad root cramfs", ++ .size =0x00D80000, ++ .offset = MTDPART_OFS_APPEND ++ + }, { +- name: "SIMpad usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d40000, ++ .name = "SIMpad local jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND ++#else ++ .name = "SIMpad root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + #endif + } + }; + #endif /* CONFIG_SA1100_SIMPAD */ + +-#ifdef CONFIG_SA1100_SIMPUTER +-#define SIMPUTER_FLASH_SIZE 0x02000000 +-static struct mtd_partition simputer_partitions[] = { +- { +- name: "blob+logo", +- offset: 0, +- size: 0x00040000 +- }, +- { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0x000C0000 +- }, +- { +- name: "/(cramfs)", +- offset: MTDPART_OFS_APPEND, +- size: 0x00200000 +- }, +- { +- name: "/usr/local(jffs2)", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL /* expand till the end */ +- } +-}; +-#endif +- + #ifdef CONFIG_SA1100_STORK +-#define STORK_FLASH_SIZE 0x02000000 + static struct mtd_partition stork_partitions[] = { + { +- name: "STORK boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "STORK boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "STORK params", +- size: 0x00040000, +- offset: 0x00040000, ++ .name = "STORK params", ++ .size = 0x00040000, ++ .offset = 0x00040000, + }, { +- name: "STORK kernel", +- size: 0x00100000, +- offset: 0x00080000, ++ .name = "STORK kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "STORK root jffs2", +- offset: 0x00180000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK root jffs2", ++ .offset = 0x00180000, ++ .size = MTDPART_SIZ_FULL, + #else +- name: "STORK initrd", +- size: 0x00100000, +- offset: 0x00180000, ++ .name = "STORK initrd", ++ .size = 0x00100000, ++ .offset = 0x00180000, + }, { +- name: "STORK root cramfs", +- size: 0x00300000, +- offset: 0x00280000, ++ .name = "STORK root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00280000, + }, { +- name: "STORK usr cramfs", +- size: 0x00800000, +- offset: 0x00580000, ++ .name = "STORK usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00580000, + }, { +- name: "STORK usr local", +- offset: 0x00d80000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK usr local", ++ .offset = 0x00d80000, ++ .size = MTDPART_SIZ_FULL, + #endif + } + }; + #endif + ++#ifdef CONFIG_SA1100_TRIZEPS ++static struct mtd_partition trizeps_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00100000, ++ .offset = 0, ++ }, { ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_YOPY +-#define YOPY_FLASH_SIZE 0x08000000 + static struct mtd_partition yopy_partitions[] = { + { +- name: "boot firmware", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "boot firmware", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00080000, ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00080000, + }, { +- name: "initrd", +- size: 0x00300000, +- offset: 0x00100000, ++ .name = "initrd", ++ .size = 0x00300000, ++ .offset = 0x00100000, + }, { +- name: "root", +- size: 0x01000000, +- offset: 0x00400000, ++ .name = "root", ++ .size = 0x01000000, ++ .offset = 0x00400000, + } + }; + #endif + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); +- +-static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; +- +-int __init sa1100_mtd_init(void) ++static int __init sa1100_static_partitions(struct mtd_partition **parts) + { +- struct mtd_partition *parts; +- int nb_parts = 0, ret; +- int parsed_nr_parts = 0; +- const char *part_type; +- unsigned long base = -1UL; +- +- /* Default flash buswidth */ +- sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; +- +- /* +- * Static partition definition selection +- */ +- part_type = "static"; ++ int nb_parts = 0; + +-#ifdef CONFIG_SA1100_ADSAGC +- if (machine_is_adsagc()) { +- parts = adsagc_partitions; +- nb_parts = ARRAY_SIZE(adsagc_partitions); +- sa1100_map.size = ADSAGC_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; +- } +-#endif + #ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { +- parts = adsbitsy_partitions; ++ *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); +- sa1100_map.size = ADSBITSY_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; +- } +-#endif +-#ifdef CONFIG_SA1100_ADSBITSYPLUS +- if (machine_is_adsbitsyplus()) { +- parts = adsbitsyplus_partitions; +- nb_parts = ARRAY_SIZE(adsbitsyplus_partitions); +- sa1100_map.size = ADSBITSYPLUS_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } + #endif + #ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { +- parts = assabet_partitions; ++ *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); +- sa1100_map.size = ASSABET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { +- parts = badge4_partitions; ++ *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); +- sa1100_map.size = BADGE4_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { +- parts = cerf_partitions; ++ *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); +- sa1100_map.size = CERF_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { +- parts = consus_partitions; ++ *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); +- sa1100_map.size = CONSUS_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { +- parts = flexanet_partitions; ++ *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); +- sa1100_map.size = FLEXANET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) { +- parts = freebird_partitions; ++ *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); +- sa1100_map.size = FREEBIRD_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { +- parts = frodo_partitions; ++ *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); +- sa1100_map.size = FRODO_FLASH_SIZE; +- base = 0x00000000; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSCLIENT + if (machine_is_graphicsclient()) { +- parts = graphicsclient_partitions; ++ *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); +- sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSMASTER + if (machine_is_graphicsmaster()) { +- parts = graphicsmaster_partitions; ++ *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); +- sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif +-#ifdef CONFIG_SA1100_H3600 +- if (machine_is_h3600()) { +- parts = h3600_partitions; +- nb_parts = ARRAY_SIZE(h3600_partitions); +- sa1100_map.size = H3600_FLASH_SIZE; +- sa1100_map.set_vpp = h3600_set_vpp; ++#ifdef CONFIG_SA1100_H3XXX ++ if (machine_is_h3xxx()) { ++ *parts = h3xxx_partitions; ++ nb_parts = ARRAY_SIZE(h3xxx_partitions); + } + #endif + #ifdef CONFIG_SA1100_HACKKIT + if (machine_is_hackkit()) { +- parts = hackkit_partitions; ++ *parts = hackkit_partitions; + nb_parts = ARRAY_SIZE(hackkit_partitions); +- sa1100_map.size = HACKKIT_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { +- parts = huw_webpanel_partitions; ++ *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); +- sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE; ++ } ++#endif ++#ifdef CONFIG_SA1100_JORNADA56X ++ if (machine_is_jornada56x()) { ++ *parts = jornada56x_partitions; ++ nb_parts = ARRAY_SIZE(jornada56x_partitions); + } + #endif + #ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { +- parts = jornada720_partitions; ++ *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); +- sa1100_map.size = JORNADA720_FLASH_SIZE; +- sa1100_map.set_vpp = jornada720_set_vpp; +- } +-#endif +-#ifdef CONFIG_SA1100_NANOENGINE +- if (machine_is_nanoengine()) { +- parts = nanoengine_partitions; +- nb_parts = ARRAY_SIZE(nanoengine_partitions); +- sa1100_map.size = NANOENGINE_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { +- parts = pangolin_partitions; ++ *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); +- sa1100_map.size = PANGOLIN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { +- parts = system3_partitions; ++ *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); +- sa1100_map.size = SYSTEM3_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { +- parts = shannon_partitions; ++ *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); +- sa1100_map.size = SHANNON_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHERMAN + if (machine_is_sherman()) { +- parts = sherman_partitions; ++ *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); +- sa1100_map.size = SHERMAN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { +- parts = simpad_partitions; ++ *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); +- sa1100_map.size = SIMPAD_FLASH_SIZE; +- } +-#endif +-#ifdef CONFIG_SA1100_SIMPUTER +- if (machine_is_simputer()) { +- parts = simputer_partitions; +- nb_parts = ARRAY_SIZE(simputer_partitions); +- sa1100_map.size = SIMPUTER_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { +- parts = stork_partitions; ++ *parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); +- sa1100_map.size = STORK_FLASH_SIZE; ++ } ++#endif ++#ifdef CONFIG_SA1100_TRIZEPS ++ if (machine_is_trizeps()) { ++ *parts = trizeps_partitions; ++ nb_parts = ARRAY_SIZE(trizeps_partitions); + } + #endif + #ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { +- parts = yopy_partitions; ++ *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); +- sa1100_map.size = YOPY_FLASH_SIZE; + } + #endif + ++ return nb_parts; ++} ++#endif ++ ++struct sa_info { ++ unsigned long base; ++ unsigned long size; ++ int width; ++ void (*set_vpp)(struct map_info *, int); ++ char name[16]; ++ struct map_info *map; ++ struct mtd_info *mtd; ++}; ++ ++#define NR_SUBMTD 4 ++ ++static struct sa_info info[NR_SUBMTD]; ++ ++static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) ++{ ++ struct mtd_info *subdev[nr]; ++ struct map_info *maps; ++ int i, found = 0, ret = 0; ++ + /* +- * For simple flash devices, use ioremap to map the flash. ++ * Allocate the map_info structs in one go. + */ +- if (base != (unsigned long)-1) { +- if (!request_mem_region(base, sa1100_map.size, "flash")) +- return -EBUSY; +- sa1100_map.map_priv_2 = base; +- sa1100_map.map_priv_1 = (unsigned long) +- ioremap(base, sa1100_map.size); ++ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); ++ if (!maps) ++ return -ENOMEM; ++ ++ memset(maps, 0, sizeof(struct map_info) * nr); ++ ++ /* ++ * Claim and then map the memory regions. ++ */ ++ for (i = 0; i < nr; i++) { ++ if (sa[i].base == (unsigned long)-1) ++ break; ++ ++ sa[i].map = maps + i; ++ sa[i].map->name = sa[i].name; ++ sprintf(sa[i].name, "sa1100-%d", i); ++ ++ if (!request_mem_region(sa[i].base, sa[i].size, sa[i].name)) { ++ i -= 1; ++ ret = -EBUSY; ++ break; ++ } ++ ++ sa[i].map->virt = ioremap(sa[i].base, sa[i].size); ++ if (!sa[i].map->virt) { + ret = -ENOMEM; +- if (!sa1100_map.map_priv_1) +- goto out_err; ++ break; + } + ++ sa[i].map->phys = sa[i].base; ++ sa[i].map->set_vpp = sa[i].set_vpp; ++ sa[i].map->bankwidth = sa[i].width; ++ sa[i].map->size = sa[i].size; ++ ++ simple_map_init(sa[i].map); ++ + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ +- printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); +- mymtd = do_map_probe("jedec_probe", &sa1100_map); +- if (!mymtd) +- mymtd = do_map_probe("cfi_probe", &sa1100_map); ++ sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); ++ if (sa[i].mtd == NULL) { + ret = -ENXIO; +- if (!mymtd) +- goto out_err; +- mymtd->module = THIS_MODULE; ++ break; ++ } ++ sa[i].mtd->owner = THIS_MODULE; ++ subdev[i] = sa[i].mtd; ++ ++ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " ++ "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, ++ sa[i].width * 8); ++ found += 1; ++ } + + /* +- * Dynamic partition selection stuff (might override the static ones) ++ * ENXIO is special. It means we didn't find a chip when ++ * we probed. We need to tear down the mapping, free the ++ * resource and mark it as such. + */ +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ if (ret == -ENXIO) { ++ iounmap(sa[i].map->virt); ++ sa[i].map->virt = NULL; ++ release_mem_region(sa[i].base, sa[i].size); ++ } + +- if (ret > 0) { +- part_type = "RedBoot"; +- parsed_nr_parts = ret; ++ /* ++ * If we found one device, don't bother with concat support. ++ * If we found multiple devices, use concat if we have it ++ * available, otherwise fail. ++ */ ++ if (ret == 0 || ret == -ENXIO) { ++ if (found == 1) { ++ *rmtd = subdev[0]; ++ ret = 0; ++ } else if (found > 1) { ++ /* ++ * We detected multiple devices. Concatenate ++ * them together. ++ */ ++#ifdef CONFIG_MTD_CONCAT ++ *rmtd = mtd_concat_create(subdev, found, ++ "sa1100"); ++ if (*rmtd == NULL) ++ ret = -ENXIO; ++#else ++ printk(KERN_ERR "SA1100 flash: multiple devices " ++ "found but MTD concat support disabled.\n"); ++ ret = -ENXIO; ++#endif ++ } + } ++ ++ /* ++ * If we failed, clean up. ++ */ ++ if (ret) { ++ do { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].map->virt) ++ iounmap(sa[i].map->virt); ++ release_mem_region(sa[i].base, sa[i].size); ++ } while (i-- > 0); ++ ++ kfree(maps); + } ++ ++ return ret; ++} ++ ++static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) ++{ ++ int i; ++ ++ del_mtd_partitions(mtd); ++ ++#ifdef CONFIG_MTD_CONCAT ++ if (mtd != sa[0].mtd) ++ mtd_concat_destroy(mtd); + #endif +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100"); +- if (ret > 0) { +- part_type = "Command Line"; +- parsed_nr_parts = ret; ++ ++ for (i = NR_SUBMTD; i >= 0; i--) { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].map->virt) ++ iounmap(sa[i].map->virt); ++ release_mem_region(sa[i].base, sa[i].size); + } ++ kfree(sa[0].map); ++} ++ ++/* ++ * A Thought: can we automatically detect the flash? ++ * - Check to see if the region is busy (yes -> failure) ++ * - Is the MSC setup for flash (no -> failure) ++ * - Probe for flash ++ */ ++static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) ++{ ++ struct map_info map; ++ struct mtd_info *mtd; ++ ++ printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", ++ phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); ++ ++ if (check_mem_region(phys, 0x08000000)) { ++ printk("busy\n"); ++ return; + } +-#endif + +- if (parsed_nr_parts > 0) { +- parts = parsed_parts; +- nb_parts = parsed_nr_parts; ++ if ((msc & 3) == 1) { ++ printk("wrong type\n"); ++ return; + } + +- if (nb_parts == 0) { +- printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); +- add_mtd_device(mymtd); +- } else { +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); +- add_mtd_partitions(mymtd, parts, nb_parts); ++ memset(&map, 0, sizeof(struct map_info)); ++ ++ map.name = "Probe"; ++ map.bankwidth = msc & MSC_RBW ? 2 : 4; ++ map.size = SZ_1M; ++ map.phys = phys; ++ map.virt = ioremap(phys, SZ_1M); ++ if (map.virt == NULL) ++ goto fail; ++ ++ simple_map_init(&map); ++ ++ /* Shame cfi_probe blurts out kernel messages... */ ++ mtd = do_map_probe("cfi_probe", &map); ++ if (mtd) ++ map_destroy(mtd); ++ iounmap(map.virt); ++ ++ if (!mtd) ++ goto fail; ++ ++ printk("pass\n"); ++ return; ++ ++ fail: ++ printk("failed\n"); ++} ++ ++static void __init sa1100_probe_flash(void) ++{ ++ printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); ++ sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); ++ sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); ++ sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); ++ sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); ++ sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); ++ sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); ++ printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); ++} ++ ++static int __init sa1100_locate_flash(void) ++{ ++ int i, nr = -ENODEV; ++ ++ sa1100_probe_flash(); ++ ++ if (machine_is_adsbitsy()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_assabet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ info[1].base = SA1100_CS1_PHYS; /* neponset */ ++ info[1].size = SZ_32M; ++ nr = 2; ++ } ++ if (machine_is_badge4()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_cerf()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_consus()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_flexanet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_freebird()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_frodo()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_graphicsclient()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_graphicsmaster()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_h3xxx()) { ++ info[0].set_vpp = h3xxx_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_huw_webpanel()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_itsy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_jornada56x()) { ++ info[0].set_vpp = jornada56x_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_jornada720()) { ++ info[0].set_vpp = jornada720_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_nanoengine()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[1].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pangolin()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_pfs168()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pleb()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_4M; ++ nr = 2; ++ } ++ if (machine_is_pt_system3()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_shannon()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ nr = 1; ++ } ++ if (machine_is_sherman()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_simpad()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_16M; ++ nr = 2; ++ } ++ if (machine_is_stork()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_trizeps()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_victor()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_2M; ++ nr = 1; ++ } ++ if (machine_is_yopy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_64M; ++ nr = 2; + } +- return 0; + +- out_err: +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++ if (nr < 0) ++ return nr; ++ ++ /* ++ * Retrieve the bankwidth from the MSC registers. ++ * We currently only implement CS0 and CS1 here. ++ */ ++ for (i = 0; i < nr; i++) { ++ switch (info[i].base) { ++ default: ++ printk(KERN_WARNING "SA1100 flash: unknown base address " ++ "0x%08lx, assuming CS0\n", info[i].base); ++ case SA1100_CS0_PHYS: ++ info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; ++ break; ++ ++ case SA1100_CS1_PHYS: ++ info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; ++ break; + } +- return ret; ++ } ++ ++ return nr; + } + +-static void __exit sa1100_mtd_cleanup(void) ++static struct mtd_partition *parsed_parts; ++const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; ++ ++static void __init sa1100_locate_partitions(struct mtd_info *mtd) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); ++ const char *part_type = NULL; ++ int nr_parts = 0; ++ ++ do { ++ /* ++ * Partition selection stuff. ++ */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); ++ if (nr_parts > 0) { ++ part_type = "dynamic"; ++ break; + } +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++#endif ++#ifdef CONFIG_MTD_SA1100_STATICMAP ++ nr_parts = sa1100_static_partitions(&parsed_parts); ++ if (nr_parts > 0) { ++ part_type = "static"; ++ break; ++ } ++#endif ++ } while (0); ++ ++ if (nr_parts == 0) { ++ printk(KERN_NOTICE "SA1100 flash: no partition info " ++ "available, registering whole flash\n"); ++ add_mtd_device(mtd); ++ } else { ++ printk(KERN_NOTICE "SA1100 flash: using %s partition " ++ "definition\n", part_type); ++ add_mtd_partitions(mtd, parsed_parts, nr_parts); + } ++ ++ /* Always succeeds. */ ++} ++ ++static void __exit sa1100_destroy_partitions(void) ++{ ++ if (parsed_parts) ++ kfree(parsed_parts); ++} ++ ++static struct mtd_info *mymtd; ++ ++static int __init sa1100_mtd_init(void) ++{ ++ int ret; ++ int nr; ++ ++ nr = sa1100_locate_flash(); ++ if (nr < 0) ++ return nr; ++ ++ ret = sa1100_setup_mtd(info, nr, &mymtd); ++ if (ret == 0) ++ sa1100_locate_partitions(mymtd); ++ ++ return ret; ++} ++ ++static void __exit sa1100_mtd_cleanup(void) ++{ ++ sa1100_destroy_mtd(info, mymtd); ++ sa1100_destroy_partitions(); + } + + module_init(sa1100_mtd_init); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/sbc8240.c +@@ -0,0 +1,247 @@ ++/* ++ * Handle mapping of the flash memory access routines on the SBC8240 board. ++ * ++ * Carolyn Smith, Tektronix, Inc. ++ * ++ * This code is GPLed ++ * ++ * $Id$ ++ * ++ */ ++ ++/* ++ * The SBC8240 has 2 flash banks. ++ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors. ++ * It contains the U-Boot code (7 sectors) and the environment (1 sector). ++ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector, ++ * 2 x 8 KiB sectors, 1 x 16 KiB sectors. ++ * Both parts are JEDEC compatible. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#include ++#endif ++ ++#define DEBUG ++ ++#ifdef DEBUG ++# define debugk(fmt,args...) printk(fmt ,##args) ++#else ++# define debugk(fmt,args...) ++#endif ++ ++ ++#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */ ++#define WINDOW_SIZE0 0x00080000 ++#define BUSWIDTH0 1 ++ ++#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */ ++#define WINDOW_SIZE1 0x00400000 ++#define BUSWIDTH1 8 ++ ++#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */ ++#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */ ++ ++ ++static struct map_info sbc8240_map[2] = { ++ { ++ .name = "sbc8240 Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .bankwidth = BUSWIDTH0, ++ }, ++ { ++ .name = "sbc8240 Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .bankwidth = BUSWIDTH1, ++ } ++}; ++ ++#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info)) ++ ++/* ++ * The following defines the partition layout of SBC8240 boards. ++ * ++ * See include/linux/mtd/partitions.h for definition of the ++ * mtd_partition structure. ++ * ++ * The *_max_flash_size is the maximum possible mapped flash size ++ * which is not necessarily the actual flash size. It must correspond ++ * to the value specified in the mapping definition defined by the ++ * "struct map_desc *_io_desc" for the corresponding machine. ++ */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++static struct mtd_partition sbc8240_uboot_partitions [] = { ++ /* Bank 0 */ ++ { ++ .name = "U-boot", /* U-Boot Firmware */ ++ .offset = 0, ++ .size = 0x00070000, /* 7 x 64 KiB sectors */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, ++ { ++ .name = "environment", /* U-Boot environment */ ++ .offset = 0x00070000, ++ .size = 0x00010000, /* 1 x 64 KiB sector */ ++ }, ++}; ++ ++static struct mtd_partition sbc8240_fs_partitions [] = { ++ { ++ .name = "jffs", /* JFFS filesystem */ ++ .offset = 0, ++ .size = 0x003C0000, /* 4 * 15 * 64KiB */ ++ }, ++ { ++ .name = "tmp32", ++ .offset = 0x003C0000, ++ .size = 0x00020000, /* 4 * 32KiB */ ++ }, ++ { ++ .name = "tmp8a", ++ .offset = 0x003E0000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp8b", ++ .offset = 0x003E8000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp16", ++ .offset = 0x003F0000, ++ .size = 0x00010000, /* 4 * 16KiB */ ++ } ++}; ++ ++#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++ ++/* trivial struct to describe partition information */ ++struct mtd_part_def ++{ ++ int nums; ++ unsigned char *type; ++ struct mtd_partition* mtd_part; ++}; ++ ++static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS]; ++static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; ++ ++ ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ ++int __init init_sbc8240_mtd (void) ++{ ++ static struct _cjs { ++ u_long addr; ++ u_long size; ++ } pt[NUM_FLASH_BANKS] = { ++ { ++ .addr = WINDOW_ADDR0, ++ .size = WINDOW_SIZE0 ++ }, ++ { ++ .addr = WINDOW_ADDR1, ++ .size = WINDOW_SIZE1 ++ }, ++ }; ++ ++ int devicesfound = 0; ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr); ++ ++ sbc8240_map[i].map_priv_1 = ++ (unsigned long) ioremap (pt[i].addr, pt[i].size); ++ if (!sbc8240_map[i].map_priv_1) { ++ printk (MSG_PREFIX "failed to ioremap\n"); ++ return -EIO; ++ } ++ simple_map_init(&sbc8240_mtd[i]); ++ ++ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]); ++ ++ if (sbc8240_mtd[i]) { ++ sbc8240_mtd[i]->module = THIS_MODULE; ++ devicesfound++; ++ } ++ } ++ ++ if (!devicesfound) { ++ printk(KERN_NOTICE MSG_PREFIX ++ "No suppported flash chips found!\n"); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions; ++ sbc8240_part_banks[0].type = "static image"; ++ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions); ++ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions; ++ sbc8240_part_banks[1].type = "static file system"; ++ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions); ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ ++ if (!sbc8240_mtd[i]) continue; ++ if (sbc8240_part_banks[i].nums == 0) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "No partition info available, registering whole device\n"); ++ add_mtd_device(sbc8240_mtd[i]); ++ } else { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name); ++ add_mtd_partitions (sbc8240_mtd[i], ++ sbc8240_part_banks[i].mtd_part, ++ sbc8240_part_banks[i].nums); ++ } ++ } ++#else ++ printk(KERN_NOTICE MSG_PREFIX ++ "Registering %d flash banks at once\n", devicesfound); ++ ++ for (i = 0; i < devicesfound; i++) { ++ add_mtd_device(sbc8240_mtd[i]); ++ } ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ return devicesfound == 0 ? -ENXIO : 0; ++} ++ ++static void __exit cleanup_sbc8240_mtd (void) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ if (sbc8240_mtd[i]) { ++ del_mtd_device (sbc8240_mtd[i]); ++ map_destroy (sbc8240_mtd[i]); ++ } ++ if (sbc8240_map[i].map_priv_1) { ++ iounmap ((void *) sbc8240_map[i].map_priv_1); ++ sbc8240_map[i].map_priv_1 = 0; ++ } ++ } ++} ++ ++module_init (init_sbc8240_mtd); ++module_exit (cleanup_sbc8240_mtd); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Carolyn Smith "); ++MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards"); ++ +--- linux-2.4.21/drivers/mtd/maps/sbc_gxx.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/sbc_gxx.c +@@ -17,7 +17,7 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +- $Id$ ++ $Id$ + + The SBC-MediaGX / SBC-GXx has up to 16 MiB of + Intel StrataFlash (28F320/28F640) in x8 mode. +@@ -84,21 +84,21 @@ + // Globals + + static volatile int page_in_window = -1; // Current page in window. +-static unsigned long iomapadr; +-static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED; ++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 } ++ { .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 +@@ -114,32 +114,12 @@ + } + + +-static __u8 sbc_gxx_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- spin_lock(&sbc_gxx_spin); +- sbc_gxx_page(map, ofs); +- ret = readb(iomapadr + (ofs & WINDOW_MASK)); +- spin_unlock(&sbc_gxx_spin); +- return ret; +-} +- +-static __u16 sbc_gxx_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- spin_lock(&sbc_gxx_spin); +- sbc_gxx_page(map, ofs); +- ret = readw(iomapadr + (ofs & WINDOW_MASK)); +- spin_unlock(&sbc_gxx_spin); +- return ret; +-} +- +-static __u32 sbc_gxx_read32(struct map_info *map, unsigned long ofs) ++static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) + { +- __u32 ret; ++ map_word ret; + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, ofs); +- ret = readl(iomapadr + (ofs & WINDOW_MASK)); ++ ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); + return ret; + } +@@ -155,33 +135,17 @@ + sbc_gxx_page(map, from); + memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); + spin_unlock(&sbc_gxx_spin); +- (__u8*)to += thislen; ++ to += thislen; + from += thislen; + len -= thislen; + } + } + +-static void sbc_gxx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- spin_lock(&sbc_gxx_spin); +- sbc_gxx_page(map, adr); +- writeb(d, iomapadr + (adr & WINDOW_MASK)); +- spin_unlock(&sbc_gxx_spin); +-} +- +-static void sbc_gxx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- spin_lock(&sbc_gxx_spin); +- sbc_gxx_page(map, adr); +- writew(d, iomapadr + (adr & WINDOW_MASK)); +- spin_unlock(&sbc_gxx_spin); +-} +- +-static void sbc_gxx_write32(struct map_info *map, __u32 d, unsigned long adr) ++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); +- writel(d, iomapadr + (adr & WINDOW_MASK)); ++ writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); + } + +@@ -203,19 +167,16 @@ + } + + static struct map_info sbc_gxx_map = { +- name: "SBC-GXx flash", +- size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount ++ .name = "SBC-GXx flash", ++ .phys = NO_XIP, ++ .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ +- buswidth: 1, +- read8: sbc_gxx_read8, +- read16: sbc_gxx_read16, +- read32: sbc_gxx_read32, +- copy_from: sbc_gxx_copy_from, +- write8: sbc_gxx_write8, +- write16: sbc_gxx_write16, +- write32: sbc_gxx_write32, +- copy_to: sbc_gxx_copy_to ++ .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. */ +@@ -228,26 +189,27 @@ + map_destroy( all_mtd ); + } + +- iounmap((void *)iomapadr); ++ iounmap(iomapadr); + release_region(PAGE_IO,PAGE_IO_SIZE); + } + +-int __init init_sbc_gxx(void) ++static int __init init_sbc_gxx(void) + { +- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { +- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", +- sbc_gxx_map.name, +- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); +- return -EAGAIN; +- } +- iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); ++ iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", + sbc_gxx_map.name ); + return -EIO; + } + +- request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" ); ++ if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { ++ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", ++ sbc_gxx_map.name, ++ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); ++ iounmap(iomapadr); ++ return -EAGAIN; ++ } ++ + + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + sbc_gxx_map.name, +@@ -261,7 +223,7 @@ + return -ENXIO; + } + +- all_mtd->module=THIS_MODULE; ++ all_mtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); +--- linux-2.4.21/drivers/mtd/maps/sc520cdp.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/sc520cdp.c +@@ -16,7 +16,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id$ ++ * $Id$ + * + * + * The SC520CDP is an evaluation board for the Elan SC520 processor available +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -84,88 +85,25 @@ + #define WINDOW_SIZE_1 0x00800000 + #define WINDOW_SIZE_2 0x00080000 + +-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + static struct map_info sc520cdp_map[] = { + { +- name: "SC520CDP Flash Bank #0", +- size: WINDOW_SIZE_0, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_0 ++ .name = "SC520CDP Flash Bank #0", ++ .size = WINDOW_SIZE_0, ++ .bankwidth = 4, ++ .phys = WINDOW_ADDR_0 + }, + { +- name: "SC520CDP Flash Bank #1", +- size: WINDOW_SIZE_1, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_1 ++ .name = "SC520CDP Flash Bank #1", ++ .size = WINDOW_SIZE_1, ++ .bankwidth = 4, ++ .phys = WINDOW_ADDR_1 + }, + { +- name: "SC520CDP DIL Flash", +- size: WINDOW_SIZE_2, +- buswidth: 1, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_2 ++ .name = "SC520CDP DIL Flash", ++ .size = WINDOW_SIZE_2, ++ .bankwidth = 1, ++ .phys = WINDOW_ADDR_2 + }, + }; + +@@ -248,16 +186,16 @@ + + static void sc520cdp_setup_par(void) + { +- volatile unsigned long *mmcr; ++ volatile unsigned long __iomem *mmcr; + unsigned long mmcr_val; + int i, j; + + /* map in SC520's MMCR area */ +- mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); ++ mmcr = ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); + if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */ +- /* force map_priv_2 fields to BIOS defaults: */ ++ /* force physical address fields to BIOS defaults: */ + for(i = 0; i < NUM_FLASH_BANKS; i++) +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + return; + } + +@@ -282,10 +220,10 @@ + sc520cdp_map[i].name); + printk(KERN_NOTICE "Trying default address 0x%lx\n", + par_table[i].default_address); +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + } + } +- iounmap((void *)mmcr); ++ iounmap(mmcr); + } + #endif + +@@ -300,13 +238,18 @@ + #endif + + for (i = 0; i < NUM_FLASH_BANKS; i++) { +- printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2); +- sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size); ++ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", ++ sc520cdp_map[i].size, sc520cdp_map[i].phys); + +- if (!sc520cdp_map[i].map_priv_1) { ++ sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); ++ ++ if (!sc520cdp_map[i].virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&sc520cdp_map[i]); ++ + mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); + if(!mymtd[i]) + mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); +@@ -314,11 +257,11 @@ + mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); + + if (mymtd[i]) { +- mymtd[i]->module = THIS_MODULE; ++ mymtd[i]->owner = THIS_MODULE; + ++devices_found; + } + else { +- iounmap((void *)sc520cdp_map[i].map_priv_1); ++ iounmap(sc520cdp_map[i].virt); + } + } + if(devices_found >= 2) { +@@ -346,9 +289,9 @@ + for (i = 0; i < NUM_FLASH_BANKS; i++) { + if (mymtd[i]) + map_destroy(mymtd[i]); +- if (sc520cdp_map[i].map_priv_1) { +- iounmap((void *)sc520cdp_map[i].map_priv_1); +- sc520cdp_map[i].map_priv_1 = 0; ++ if (sc520cdp_map[i].virt) { ++ iounmap(sc520cdp_map[i].virt); ++ sc520cdp_map[i].virt = NULL; + } + } + } +--- linux-2.4.21/drivers/mtd/maps/scb2_flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/scb2_flash.c +@@ -1,6 +1,6 @@ + /* + * MTD map driver for BIOS Flash on Intel SCB2 boards +- * $Id$ ++ * $Id$ + * Copyright (C) 2002 Sun Microsystems, Inc. + * Tim Hockin + * +@@ -14,7 +14,7 @@ + * try to request it here, but if it fails, we carry on anyway. + * + * This is how the chip is attached, so said the schematic: +- * * a 4 MiB (32 Mb) 16 bit chip ++ * * a 4 MiB (32 Mib) 16 bit chip + * * a 1 MiB memory region + * * A20 and A21 pulled up + * * D8-D15 ignored +@@ -31,23 +31,24 @@ + * + * The actual BIOS layout has been mostly reverse engineered. Intel BIOS + * updates for this board include 10 related (*.bio - &.bi9) binary files and +- * another seperate (*.bbo) binary file. The 10 files are 64k of data + a ++ * 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 +- * seperate .bbo file also has a small header. It is the 'Boot Block' ++ * 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 seperate ++ * Intel's BIOS update program flashes the BIOS and Boot Block in separate + * steps. Probably a wise thing to do. + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -60,65 +61,13 @@ + #define SCB2_ADDR 0xfff00000 + #define SCB2_WINDOW 0x00100000 + +-static __u8 scb2_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scb2_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 scb2_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void scb2_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + +-static void *scb2_ioaddr; ++static void __iomem *scb2_ioaddr; + static struct mtd_info *scb2_mtd; +-struct map_info scb2_map = { +- name: "SCB2 BIOS Flash", +- size: 0, +- buswidth: 1, +- read8: scb2_read8, +- read16: scb2_read16, +- read32: scb2_read32, +- copy_from: scb2_copy_from, +- write8: scb2_write8, +- write16: scb2_write16, +- write32: scb2_write32, +- copy_to: scb2_copy_to, ++static struct map_info scb2_map = { ++ .name = "SCB2 BIOS Flash", ++ .size = 0, ++ .bankwidth = 1, + }; + static int region_fail; + +@@ -137,6 +86,8 @@ + return -1; + } + ++ /* I wasn't here. I didn't see. dwmw2. */ ++ + /* the chip is sometimes bigger than the map - what a waste */ + mtd->size = map->size; + +@@ -211,9 +162,12 @@ + return -ENOMEM; + } + +- scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr; ++ scb2_map.phys = SCB2_ADDR; ++ scb2_map.virt = scb2_ioaddr; + scb2_map.size = SCB2_WINDOW; + ++ simple_map_init(&scb2_map); ++ + /* try to find a chip */ + scb2_mtd = do_map_probe("cfi_probe", &scb2_map); + +@@ -225,7 +179,7 @@ + return -ENODEV; + } + +- scb2_mtd->module = THIS_MODULE; ++ scb2_mtd->owner = THIS_MODULE; + if (scb2_fixup_mtd(scb2_mtd) < 0) { + del_mtd_device(scb2_mtd); + map_destroy(scb2_mtd); +@@ -235,7 +189,7 @@ + return -ENODEV; + } + +- printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n", ++ printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", + scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); + + add_mtd_device(scb2_mtd); +@@ -264,21 +218,21 @@ + pci_set_drvdata(dev, NULL); + } + +-static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = { ++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 ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID + }, + { 0, } + }; + + static struct pci_driver scb2_flash_driver = { +- name: "Intel SCB2 BIOS Flash", +- id_table: scb2_flash_pci_ids, +- probe: scb2_flash_probe, +- remove: __devexit_p(scb2_flash_remove), ++ .name = "Intel SCB2 BIOS Flash", ++ .id_table = scb2_flash_pci_ids, ++ .probe = scb2_flash_probe, ++ .remove = __devexit_p(scb2_flash_remove), + }; + + static int __init +--- linux-2.4.21/drivers/mtd/maps/scx200_docflash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/scx200_docflash.c +@@ -2,7 +2,7 @@ + + Copyright (c) 2001,2002 Christer Weinigel + +- $Id$ ++ $Id$ + + National Semiconductor SCx200 flash mapped with DOCCS + */ +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -75,49 +76,12 @@ + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + #endif + +-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info scx200_docflash_map = { + .name = "NatSemi SCx200 DOCCS Flash", +- .read8 = scx200_docflash_read8, +- .read16 = scx200_docflash_read16, +- .copy_from = scx200_docflash_copy_from, +- .write8 = scx200_docflash_write8, +- .write16 = scx200_docflash_write16, +- .copy_to = scx200_docflash_copy_to + }; + +-int __init init_scx200_docflash(void) ++static int __init init_scx200_docflash(void) + { + unsigned u; + unsigned base; +@@ -209,12 +173,15 @@ + + scx200_docflash_map.size = size; + if (width == 8) +- scx200_docflash_map.buswidth = 1; ++ scx200_docflash_map.bankwidth = 1; + else +- scx200_docflash_map.buswidth = 2; ++ scx200_docflash_map.bankwidth = 2; + +- scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); +- if (!scx200_docflash_map.map_priv_1) { ++ simple_map_init(&scx200_docflash_map); ++ ++ scx200_docflash_map.phys = docmem.start; ++ scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size); ++ if (!scx200_docflash_map.virt) { + printk(KERN_ERR NAME ": failed to ioremap the flash\n"); + release_resource(&docmem); + return -EIO; +@@ -223,7 +190,7 @@ + mymtd = do_map_probe(flashtype, &scx200_docflash_map); + if (!mymtd) { + printk(KERN_ERR NAME ": unable to detect flash\n"); +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ iounmap(scx200_docflash_map.virt); + release_resource(&docmem); + return -ENXIO; + } +@@ -231,7 +198,7 @@ + if (size < mymtd->size) + printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #if PARTITION + partition_info[3].offset = mymtd->size-partition_info[3].size; +@@ -253,8 +220,8 @@ + #endif + map_destroy(mymtd); + } +- if (scx200_docflash_map.map_priv_1) { +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ if (scx200_docflash_map.virt) { ++ iounmap(scx200_docflash_map.virt); + release_resource(&docmem); + } + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/sharpsl-flash.c +@@ -0,0 +1,101 @@ ++/* ++ * sharpsl-flash.c ++ * ++ * Copyright (C) 2001 Lineo Japan, Inc. ++ * Copyright (C) 2002 SHARP ++ * ++ * $Id$ ++ * ++ * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp ++ * Handle mapping of the flash on the RPX Lite and CLLF boards ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WINDOW_ADDR 0x00000000 ++#define WINDOW_SIZE 0x01000000 ++#define BANK_WIDTH 2 ++ ++static struct mtd_info *mymtd; ++ ++struct map_info sharpsl_map = { ++ .name = "sharpsl-flash", ++ .size = WINDOW_SIZE, ++ .bankwidth = BANK_WIDTH, ++ .phys = WINDOW_ADDR ++}; ++ ++static struct mtd_partition sharpsl_partitions[1] = { ++ { ++ name: "Filesystem", ++ size: 0x006d0000, ++ offset: 0x00120000 ++ } ++}; ++ ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++ ++int __init init_sharpsl(void) ++{ ++ struct mtd_partition *parts; ++ int nb_parts = 0; ++ char *part_type = "static"; ++ ++ printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); ++ sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ if (!sharpsl_map.virt) { ++ printk("Failed to ioremap\n"); ++ return -EIO; ++ } ++ mymtd = do_map_probe("map_rom", &sharpsl_map); ++ if (!mymtd) { ++ iounmap(sharpsl_map.virt); ++ return -ENXIO; ++ } ++ ++ mymtd->owner = THIS_MODULE; ++ ++ parts = sharpsl_partitions; ++ nb_parts = NB_OF(sharpsl_partitions); ++ ++ printk(KERN_NOTICE "Using %s partision definition\n", part_type); ++ add_mtd_partitions(mymtd, parts, nb_parts); ++ ++ return 0; ++} ++ ++static void __exit cleanup_sharpsl(void) ++{ ++ if (mymtd) { ++ del_mtd_partitions(mymtd); ++ map_destroy(mymtd); ++ } ++ if (sharpsl_map.virt) { ++ iounmap(sharpsl_map.virt); ++ sharpsl_map.virt = 0; ++ } ++} ++ ++module_init(init_sharpsl); ++module_exit(cleanup_sharpsl); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("SHARP (Original: Arnold Christensen )"); ++MODULE_DESCRIPTION("MTD map driver for SHARP SL series"); +--- linux-2.4.21/drivers/mtd/maps/solutionengine.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/solutionengine.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Flash and EPROM on Hitachi Solution Engine and similar boards. + * +@@ -11,31 +11,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +- +- +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-__u32 soleng_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- ++#include + + static struct mtd_info *flash_mtd; + static struct mtd_info *eprom_mtd; +@@ -43,35 +25,33 @@ + static struct mtd_partition *parsed_parts; + + struct map_info soleng_eprom_map = { +- name: "Solution Engine EPROM", +- size: 0x400000, +- buswidth: 4, +- copy_from: soleng_copy_from, ++ .name = "Solution Engine EPROM", ++ .size = 0x400000, ++ .bankwidth = 4, + }; + + struct map_info soleng_flash_map = { +- name: "Solution Engine FLASH", +- size: 0x400000, +- buswidth: 4, +- read32: soleng_read32, +- copy_from: soleng_copy_from, +- write32: soleng_write32, ++ .name = "Solution Engine FLASH", ++ .size = 0x400000, ++ .bankwidth = 4, + }; + ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ + #ifdef CONFIG_MTD_SUPERH_RESERVE + static struct mtd_partition superh_se_partitions[] = { + /* Reserved for boot code, read-only */ + { +- name: "flash_boot", +- offset: 0x00000000, +- size: CONFIG_MTD_SUPERH_RESERVE, +- mask_flags: MTD_WRITEABLE, ++ .name = "flash_boot", ++ .offset = 0x00000000, ++ .size = CONFIG_MTD_SUPERH_RESERVE, ++ .mask_flags = MTD_WRITEABLE, + }, + /* All else is writable (e.g. JFFS) */ + { +- name: "Flash FS", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "Flash FS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + } + }; + #endif /* CONFIG_MTD_SUPERH_RESERVE */ +@@ -81,16 +61,22 @@ + int nr_parts = 0; + + /* First probe at offset 0 */ +- soleng_flash_map.map_priv_1 = P2SEGADDR(0); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000); ++ soleng_flash_map.phys = 0; ++ soleng_flash_map.virt = (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.map_priv_1 = P2SEGADDR(0x01000000); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0); ++ soleng_flash_map.phys = 0x01000000; ++ soleng_flash_map.virt = P2SEGADDR(0x01000000); ++ soleng_eprom_map.phys = 0; ++ soleng_eprom_map.virt = P1SEGADDR(0); + flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); + if (!flash_mtd) { + /* Eep. */ +@@ -99,25 +85,20 @@ + } + } + printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n", +- soleng_flash_map.map_priv_1 & 0x1fffffff, +- soleng_eprom_map.map_priv_1 & 0x1fffffff); +- flash_mtd->module = THIS_MODULE; ++ soleng_flash_map.phys & 0x1fffffff, ++ soleng_eprom_map.phys & 0x1fffffff); ++ flash_mtd->owner = THIS_MODULE; + + eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map); + if (eprom_mtd) { +- eprom_mtd->module = THIS_MODULE; ++ eprom_mtd->owner = THIS_MODULE; + add_mtd_device(eprom_mtd); + } + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); +- if (nr_parts > 0) +- printk(KERN_NOTICE "Found RedBoot partition table.\n"); +- else if (nr_parts < 0) +- printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +-#endif /* CONFIG_MTD_REDBOOT_PARTS */ +-#if CONFIG_MTD_SUPERH_RESERVE +- if (nr_parts == 0) { ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); ++ ++#ifdef CONFIG_MTD_SUPERH_RESERVE ++ if (nr_parts <= 0) { + printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", + CONFIG_MTD_SUPERH_RESERVE); + parsed_parts = superh_se_partitions; +--- linux-2.4.21/drivers/mtd/maps/sun_uflash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/sun_uflash.c +@@ -1,4 +1,4 @@ +-/* $Id$ ++/* $Id$ + * + * sun_uflash - Driver implementation for user-programmable flash + * present on many Sun Microsystems SME boardsets. +@@ -12,7 +12,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -48,60 +47,11 @@ + struct list_head list; + }; + +-__u8 uflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readb(map->map_priv_1 + ofs)); +-} +- +-__u16 uflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readw(map->map_priv_1 + ofs)); +-} +- +-__u32 uflash_read32(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readl(map->map_priv_1 + ofs)); +-} +- +-void uflash_copy_from(struct map_info *map, void *to, unsigned long from, +- ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +-} +- +-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +-} +- +-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +-} +- +-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from, +- ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info uflash_map_templ = { +- name: "SUNW,???-????", +- size: UFLASH_WINDOW_SIZE, +- buswidth: UFLASH_BUSWIDTH, +- read8: uflash_read8, +- read16: uflash_read16, +- read32: uflash_read32, +- copy_from: uflash_copy_from, +- write8: uflash_write8, +- write16: uflash_write16, +- write32: uflash_write32, +- copy_to: uflash_copy_to ++ .name = "SUNW,???-????", ++ .size = UFLASH_WINDOW_SIZE, ++ .bankwidth = UFLASH_BUSWIDTH, + }; + + int uflash_devinit(struct linux_ebus_device* edev) +@@ -145,20 +95,21 @@ + if(0 != pdev->name && 0 < strlen(pdev->name)) { + pdev->map.name = pdev->name; + } +- +- pdev->map.map_priv_1 = +- (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size); +- if(0 == pdev->map.map_priv_1) { ++ pdev->map.phys = edev->resource[0].start; ++ pdev->map.virt = ioremap_nocache(edev->resource[0].start, pdev->map.size); ++ if(0 == pdev->map.virt) { + printk("%s: failed to map device\n", __FUNCTION__); + kfree(pdev->name); + kfree(pdev); + return(-1); + } + ++ simple_map_init(&pdev->map); ++ + /* MTD registration */ + pdev->mtd = do_map_probe("cfi_probe", &pdev->map); + if(0 == pdev->mtd) { +- iounmap((void *)pdev->map.map_priv_1); ++ iounmap((void *)pdev->map.virt); + kfree(pdev->name); + kfree(pdev); + return(-ENXIO); +@@ -166,7 +117,7 @@ + + list_add(&pdev->list, &device_list); + +- pdev->mtd->module = THIS_MODULE; ++ pdev->mtd->owner = THIS_MODULE; + + add_mtd_device(pdev->mtd); + return(0); +@@ -211,9 +162,9 @@ + del_mtd_device(udev->mtd); + map_destroy(udev->mtd); + } +- if(0 != udev->map.map_priv_1) { +- iounmap((void*)udev->map.map_priv_1); +- udev->map.map_priv_1 = 0; ++ if(0 != udev->map.virt) { ++ iounmap((void*)udev->map.virt); ++ udev->map.virt = 0; + } + if(0 != udev->name) { + kfree(udev->name); +--- linux-2.4.21/drivers/mtd/maps/tqm8xxl.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/tqm8xxl.c +@@ -2,7 +2,7 @@ + * Handle mapping of the flash memory access routines + * on TQM8xxL based devices. + * +- * $Id$ ++ * $Id$ + * + * based on rpxlite.c + * +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -49,47 +50,7 @@ + static struct map_info* map_banks[FLASH_BANK_MAX]; + static struct mtd_part_def part_banks[FLASH_BANK_MAX]; + static unsigned long num_banks; +-static unsigned long start_scan_addr; +- +-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs) +-{ +- return *((__u8 *)(map->map_priv_1 + ofs)); +-} +- +-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs) +-{ +- return *((__u16 *)(map->map_priv_1 + ofs)); +-} +- +-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs) +-{ +- return *((__u32 *)(map->map_priv_1 + ofs)); +-} +- +-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *)( map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} ++static void __iomem *start_scan_addr; + + /* + * Here are partition information for all known TQM8xxL series devices. +@@ -107,50 +68,48 @@ + static unsigned long tqm8xxl_max_flash_size = 0x00800000; + + /* partition definition for first flash bank +- * also ref. to "drivers\char\flash_config.c" ++ * (cf. "drivers/char/flash_config.c") + */ + static struct mtd_partition tqm8xxl_partitions[] = { + { +- name: "ppcboot", +- offset: 0x00000000, +- size: 0x00020000, /* 128KB */ +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ppcboot", ++ .offset = 0x00000000, ++ .size = 0x00020000, /* 128KB */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "kernel", /* default kernel image */ +- offset: 0x00020000, +- size: 0x000e0000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "kernel", /* default kernel image */ ++ .offset = 0x00020000, ++ .size = 0x000e0000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "user", +- offset: 0x00100000, +- size: 0x00100000, ++ .name = "user", ++ .offset = 0x00100000, ++ .size = 0x00100000, + }, + { +- name: "initrd", +- offset: 0x00200000, +- size: 0x00200000, ++ .name = "initrd", ++ .offset = 0x00200000, ++ .size = 0x00200000, + } + }; +-/* partition definition for second flahs bank */ ++/* partition definition for second flash bank */ + static struct mtd_partition tqm8xxl_fs_partitions[] = { + { +- name: "cramfs", +- offset: 0x00000000, +- size: 0x00200000, ++ .name = "cramfs", ++ .offset = 0x00000000, ++ .size = 0x00200000, + }, + { +- name: "jffs", +- offset: 0x00200000, +- size: 0x00200000, +- //size: MTDPART_SIZ_FULL, ++ .name = "jffs", ++ .offset = 0x00200000, ++ .size = 0x00200000, ++ //.size = MTDPART_SIZ_FULL, + } + }; + #endif + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + int __init init_tqm_mtd(void) + { + int idx = 0, ret = 0; +@@ -160,67 +119,73 @@ + + flash_addr = bd->bi_flashstart; + flash_size = bd->bi_flashsize; +- //request maximum flash size address spzce +- start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); ++ ++ //request maximum flash size address space ++ start_scan_addr = ioremap(flash_addr, flash_size); + if (!start_scan_addr) { +- //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR); +- printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); ++ printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + return -EIO; + } +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { ++ ++ for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { + if(mtd_size >= flash_size) + break; + +- printk("%s: chip probing count %d\n", __FUNCTION__, idx); ++ printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); + + map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL); +- if(map_banks[idx] == NULL) +- { +- //return -ENOMEM; ++ if(map_banks[idx] == NULL) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } ++ + memset((void *)map_banks[idx], 0, sizeof(struct map_info)); + map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); +- if(map_banks[idx]->name == NULL) +- { +- //return -ENOMEM; ++ ++ if (!map_banks[idx]->name) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } +- memset((void *)map_banks[idx]->name, 0, 16); +- + sprintf(map_banks[idx]->name, "TQM8xxL%d", idx); ++ + map_banks[idx]->size = flash_size; +- map_banks[idx]->buswidth = 4; +- map_banks[idx]->read8 = tqm8xxl_read8; +- map_banks[idx]->read16 = tqm8xxl_read16; +- map_banks[idx]->read32 = tqm8xxl_read32; +- map_banks[idx]->copy_from = tqm8xxl_copy_from; +- map_banks[idx]->write8 = tqm8xxl_write8; +- map_banks[idx]->write16 = tqm8xxl_write16; +- map_banks[idx]->write32 = tqm8xxl_write32; +- map_banks[idx]->copy_to = tqm8xxl_copy_to; ++ map_banks[idx]->bankwidth = 4; ++ ++ simple_map_init(map_banks[idx]); ++ ++ map_banks[idx]->virt = start_scan_addr; ++ map_banks[idx]->phys = flash_addr; ++ /* FIXME: This looks utterly bogus, but I'm trying to ++ preserve the behaviour of the original (shown here)... ++ + map_banks[idx]->map_priv_1 = + start_scan_addr + ((idx > 0) ? + (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); ++ */ ++ ++ if (idx && mtd_banks[idx-1]) { ++ map_banks[idx]->virt += mtd_banks[idx-1]->size; ++ map_banks[idx]->phys += mtd_banks[idx-1]->size; ++ } ++ + //start to probe flash chips + mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); +- if(mtd_banks[idx]) +- { +- mtd_banks[idx]->module = THIS_MODULE; ++ ++ if (mtd_banks[idx]) { ++ mtd_banks[idx]->owner = THIS_MODULE; + mtd_size += mtd_banks[idx]->size; + num_banks++; +- printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, ++ ++ printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + mtd_banks[idx]->name, mtd_banks[idx]->size); + } + } + + /* no supported flash chips found */ +- if(!num_banks) +- { +- printk("TQM8xxL: No support flash chips found!\n"); ++ if (!num_banks) { ++ printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n"); + ret = -ENXIO; + goto error_mem; + } +@@ -231,12 +196,13 @@ + */ + part_banks[0].mtd_part = tqm8xxl_partitions; + part_banks[0].type = "Static image"; +- part_banks[0].nums = NB_OF(tqm8xxl_partitions); ++ part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions); ++ + part_banks[1].mtd_part = tqm8xxl_fs_partitions; + part_banks[1].type = "Static file system"; +- part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions); +- for(idx = 0; idx < num_banks ; idx++) +- { ++ part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions); ++ ++ for(idx = 0; idx < num_banks ; idx++) { + if (part_banks[idx].nums == 0) { + printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx); + add_mtd_device(mtd_banks[idx]); +@@ -254,12 +220,9 @@ + #endif + return 0; + error_mem: +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { +- if(map_banks[idx] != NULL) +- { +- if(map_banks[idx]->name != NULL) +- { ++ for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { ++ if(map_banks[idx] != NULL) { ++ if(map_banks[idx]->name != NULL) { + kfree(map_banks[idx]->name); + map_banks[idx]->name = NULL; + } +@@ -267,18 +230,15 @@ + map_banks[idx] = NULL; + } + } +- //return -ENOMEM; + error: +- iounmap((void *)start_scan_addr); +- //return -ENXIO; ++ iounmap(start_scan_addr); + return ret; + } + + static void __exit cleanup_tqm_mtd(void) + { + unsigned int idx = 0; +- for(idx = 0 ; idx < num_banks ; idx++) +- { ++ for(idx = 0 ; idx < num_banks ; idx++) { + /* destroy mtd_info previously allocated */ + if (mtd_banks[idx]) { + del_mtd_partitions(mtd_banks[idx]); +@@ -288,8 +248,9 @@ + kfree(map_banks[idx]->name); + kfree(map_banks[idx]); + } ++ + if (start_scan_addr) { +- iounmap((void *)start_scan_addr); ++ iounmap(start_scan_addr); + start_scan_addr = 0; + } + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/ts5500_flash.c +@@ -0,0 +1,141 @@ ++/* ++ * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board ++ * ++ * Copyright (C) 2004 Sean Young ++ * ++ * 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 a proprietary FTL from General Software which isn't ++ * supported as of yet so standard drives can't be mounted; you can create ++ * your own (e.g. jffs) file system. ++ * - 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. ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#include ++#endif ++ ++#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 ++}; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++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 (sizeof(ts5500_partitions)/sizeof(struct mtd_partition)) ++ ++#endif ++ ++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 err_out_ioremap; ++ } ++ ++ 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 err_out_map; ++ } ++ ++ mymtd->owner = THIS_MODULE; ++#ifdef CONFIG_MTD_PARTITIONS ++ add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS); ++#else ++ add_mtd_device(mymtd); ++#endif ++ ++ return 0; ++ ++err_out_map: ++ map_destroy(mymtd); ++err_out_ioremap: ++ iounmap(ts5500_map.virt); ++ ++ return rc; ++} ++ ++static void __exit cleanup_ts5500_map(void) ++{ ++ if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ del_mtd_partitions(mymtd); ++#else ++ del_mtd_device(mymtd); ++#endif ++ 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 "); ++MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board"); ++ +--- linux-2.4.21/drivers/mtd/maps/tsunami_flash.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/tsunami_flash.c +@@ -2,25 +2,29 @@ + * tsunami_flash.c + * + * flash chip on alpha ds10... +- * $Id$ ++ * $Id$ + */ + #include + #include ++#include + #include ++#include + + #define FLASH_ENABLE_PORT 0x00C00001 + #define FLASH_ENABLE_BYTE 0x01 + #define FLASH_DISABLE_BYTE 0x00 + + #define MAX_TIG_FLASH_SIZE (12*1024*1024) +-static inline __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset) ++static inline map_word tsunami_flash_read8(struct map_info *map, unsigned long offset) + { +- return tsunami_tig_readb(offset); ++ map_word val; ++ val.x[0] = tsunami_tig_readb(offset); ++ return val; + } + +-static void tsunami_flash_write8(struct map_info *map, __u8 value, unsigned long offset) ++static void tsunami_flash_write8(struct map_info *map, map_word value, unsigned long offset) + { +- tsunami_tig_writeb(value, offset); ++ tsunami_tig_writeb(value.x[0], offset); + } + + static void tsunami_flash_copy_from( +@@ -58,18 +62,12 @@ + static struct map_info tsunami_flash_map = { + .name = "flash chip on the Tsunami TIG bus", + .size = MAX_TIG_FLASH_SIZE, +- .buswidth = 1, +- .read8 = tsunami_flash_read8, +- .read16 = 0, +- .read32 = 0, ++ .phys = NO_XIP; ++ .bankwidth = 1, ++ .read = tsunami_flash_read8, + .copy_from = tsunami_flash_copy_from, +- .write8 = tsunami_flash_write8, +- .write16 = 0, +- .write32 = 0, ++ .write = tsunami_flash_write8, + .copy_to = tsunami_flash_copy_to, +- .set_vpp = 0, +- .map_priv_1 = 0, +- + }; + + static struct mtd_info *tsunami_flash_mtd; +@@ -88,7 +86,7 @@ + + static int __init init_tsunami_flash(void) + { +- static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; ++ static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; + char **type; + + tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT); +@@ -99,7 +97,7 @@ + tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map); + } + if (tsunami_flash_mtd) { +- tsunami_flash_mtd->module = THIS_MODULE; ++ tsunami_flash_mtd->owner = THIS_MODULE; + add_mtd_device(tsunami_flash_mtd); + return 0; + } +--- linux-2.4.21/drivers/mtd/maps/uclinux.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/uclinux.c +@@ -5,7 +5,7 @@ + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * +- * $Id$ ++ * $Id$ + */ + + /****************************************************************************/ +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -24,58 +25,11 @@ + + /****************************************************************************/ + +-__u8 uclinux_read8(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u8 *) (map->map_priv_1 + ofs))); +-} +- +-__u16 uclinux_read16(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u16 *) (map->map_priv_1 + ofs))); +-} +- +-__u32 uclinux_read32(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u32 *) (map->map_priv_1 + ofs))); +-} +- +-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (map->map_priv_1 + to), from, len); +-} + + /****************************************************************************/ + + struct map_info uclinux_ram_map = { +- name: "RAM", +- read8: uclinux_read8, +- read16: uclinux_read16, +- read32: uclinux_read32, +- copy_from: uclinux_copy_from, +- write8: uclinux_write8, +- write16: uclinux_write16, +- write32: uclinux_write32, +- copy_to: uclinux_copy_to, ++ .name = "RAM", + }; + + struct mtd_info *uclinux_ram_mtdinfo; +@@ -83,7 +37,7 @@ + /****************************************************************************/ + + struct mtd_partition uclinux_romfs[] = { +- { name: "ROMfs", offset: 0 } ++ { .name = "ROMfs" } + }; + + #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) +@@ -93,8 +47,8 @@ + int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf) + { +- struct map_info *map = (struct map_info *) mtd->priv; +- *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from)); ++ struct map_info *map = mtd->priv; ++ *mtdbuf = (u_char *) (map->virt + ((int) from)); + *retlen = len; + return(0); + } +@@ -108,29 +62,30 @@ + extern char _ebss; + + mapp = &uclinux_ram_map; +- mapp->map_priv_2 = (unsigned long) &_ebss; ++ mapp->phys = (unsigned long) &_ebss; + mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); +- mapp->buswidth = 4; ++ mapp->bankwidth = 4; + + printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", + (int) mapp->map_priv_2, (int) mapp->size); + +- mapp->map_priv_1 = (unsigned long) +- ioremap_nocache(mapp->map_priv_2, mapp->size); ++ mapp->virt = ioremap_nocache(mapp->phys, mapp->size); + +- if (mapp->map_priv_1 == 0) { ++ if (mapp->virt == 0) { + printk("uclinux[mtd]: ioremap_nocache() failed\n"); + return(-EIO); + } + ++ simple_map_init(mapp); ++ + mtd = do_map_probe("map_ram", mapp); + if (!mtd) { + printk("uclinux[mtd]: failed to find a mapping?\n"); +- iounmap((void *) mapp->map_priv_1); ++ iounmap(mapp->virt); + return(-ENXIO); + } + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->point = uclinux_point; + mtd->priv = mapp; + +@@ -155,8 +110,8 @@ + uclinux_ram_mtdinfo = NULL; + } + if (uclinux_ram_map.map_priv_1) { +- iounmap((void *) uclinux_ram_map.map_priv_1); +- uclinux_ram_map.map_priv_1 = 0; ++ iounmap((void *) uclinux_ram_map.virt); ++ uclinux_ram_map.virt = 0; + } + } + +--- linux-2.4.21/drivers/mtd/maps/vmax301.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/maps/vmax301.c +@@ -1,4 +1,4 @@ +-// $Id$ ++// $Id$ + /* ###################################################################### + + Tempustech VMAX SBC301 MTD Driver. +@@ -24,6 +24,7 @@ + #include + + #include ++#include + + + #define WINDOW_START 0xd8000 +@@ -37,7 +38,7 @@ + the extra indirection from having one of the map->map_priv + fields pointing to yet another private struct. + */ +-static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED; ++static DEFINE_SPINLOCK(vmax301_spin); + + static void __vmax301_page(struct map_info *map, unsigned long page) + { +@@ -53,32 +54,12 @@ + __vmax301_page(map, page); + } + +-static __u8 vmax301_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- spin_lock(&vmax301_spin); +- vmax301_page(map, ofs); +- ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); +- spin_unlock(&vmax301_spin); +- return ret; +-} +- +-static __u16 vmax301_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- spin_lock(&vmax301_spin); +- vmax301_page(map, ofs); +- ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK)); +- spin_unlock(&vmax301_spin); +- return ret; +-} +- +-static __u32 vmax301_read32(struct map_info *map, unsigned long ofs) ++static map_word vmax301_read8(struct map_info *map, unsigned long ofs) + { +- __u32 ret; ++ map_word ret; + spin_lock(&vmax301_spin); + vmax301_page(map, ofs); +- ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK)); ++ ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); + spin_unlock(&vmax301_spin); + return ret; + } +@@ -99,27 +80,11 @@ + } + } + +-static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- spin_lock(&vmax301_spin); +- vmax301_page(map, adr); +- writeb(d, map->map_priv_2 + (adr & WINDOW_MASK)); +- spin_unlock(&vmax301_spin); +-} +- +-static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- spin_lock(&vmax301_spin); +- vmax301_page(map, adr); +- writew(d, map->map_priv_2 + (adr & WINDOW_MASK)); +- spin_unlock(&vmax301_spin); +-} +- +-static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr) ++static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr) + { + spin_lock(&vmax301_spin); + vmax301_page(map, adr); +- writel(d, map->map_priv_2 + (adr & WINDOW_MASK)); ++ writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK)); + spin_unlock(&vmax301_spin); + } + +@@ -142,34 +107,28 @@ + + static struct map_info vmax_map[2] = { + { +- name: "VMAX301 Internal Flash", +- size: 3*2*1024*1024, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + WINDOW_LENGTH, +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Internal Flash", ++ .phys = NO_XIP, ++ .size = 3*2*1024*1024, ++ .bankwidth = 1, ++ .read = vmax301_read8, ++ .copy_from = vmax301_copy_from, ++ .write = vmax301_write8, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + WINDOW_LENGTH, ++ .map_priv_2 = 0xFFFFFFFF + }, + { +- name: "VMAX301 Socket", +- size: 0, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH), +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Socket", ++ .phys = NO_XIP, ++ .size = 0, ++ .bankwidth = 1, ++ .read = vmax301_read8, ++ .copy_from = vmax301_copy_from, ++ .write = vmax301_write8, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), ++ .map_priv_2 = 0xFFFFFFFF + } + }; + +@@ -206,8 +165,8 @@ + address of the first half, because it's used more + often. + */ +- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START; +- vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START); ++ vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; ++ vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); + + for (i=0; i<2; i++) { + vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); +@@ -218,7 +177,7 @@ + if (!vmax_mtd[i]) + vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); + if (vmax_mtd[i]) { +- vmax_mtd[i]->module = THIS_MODULE; ++ vmax_mtd[i]->owner = THIS_MODULE; + add_mtd_device(vmax_mtd[i]); + } + } +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/walnut.c +@@ -0,0 +1,122 @@ ++/* ++ * $Id$ ++ * ++ * Mapping for Walnut flash ++ * (used ebony.c as a "framework") ++ * ++ * Heikki Lindholm ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* these should be in platforms/4xx/walnut.h ? */ ++#define WALNUT_FLASH_ONBD_N(x) (x & 0x02) ++#define WALNUT_FLASH_SRAM_SEL(x) (x & 0x01) ++#define WALNUT_FLASH_LOW 0xFFF00000 ++#define WALNUT_FLASH_HIGH 0xFFF80000 ++#define WALNUT_FLASH_SIZE 0x80000 ++ ++static struct mtd_info *flash; ++ ++static struct map_info walnut_map = { ++ .name = "Walnut flash", ++ .size = WALNUT_FLASH_SIZE, ++ .bankwidth = 1, ++}; ++ ++/* Actually, OpenBIOS is the last 128 KiB of the flash - better ++ * partitioning could be made */ ++static struct mtd_partition walnut_partitions[] = { ++ { ++ .name = "OpenBIOS", ++ .offset = 0x0, ++ .size = WALNUT_FLASH_SIZE, ++ /*.mask_flags = MTD_WRITEABLE, */ /* force read-only */ ++ } ++}; ++ ++int __init init_walnut(void) ++{ ++ u8 fpga_brds1; ++ void *fpga_brds1_adr; ++ void *fpga_status_adr; ++ unsigned long flash_base; ++ ++ /* this should already be mapped (platform/4xx/walnut.c) */ ++ fpga_status_adr = ioremap(WALNUT_FPGA_BASE, 8); ++ if (!fpga_status_adr) ++ return -ENOMEM; ++ ++ fpga_brds1_adr = fpga_status_adr+5; ++ fpga_brds1 = readb(fpga_brds1_adr); ++ /* iounmap(fpga_status_adr); */ ++ ++ if (WALNUT_FLASH_ONBD_N(fpga_brds1)) { ++ printk("The on-board flash is disabled (U79 sw 5)!"); ++ return -EIO; ++ } ++ if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) ++ flash_base = WALNUT_FLASH_LOW; ++ else ++ flash_base = WALNUT_FLASH_HIGH; ++ ++ walnut_map.phys = flash_base; ++ walnut_map.virt = ++ (void __iomem *)ioremap(flash_base, walnut_map.size); ++ ++ if (!walnut_map.virt) { ++ printk("Failed to ioremap flash.\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&walnut_map); ++ ++ flash = do_map_probe("jedec_probe", &walnut_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, walnut_partitions, ++ ARRAY_SIZE(walnut_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit cleanup_walnut(void) ++{ ++ if (flash) { ++ del_mtd_partitions(flash); ++ map_destroy(flash); ++ } ++ ++ if (walnut_map.virt) { ++ iounmap((void *)walnut_map.virt); ++ walnut_map.virt = 0; ++ } ++} ++ ++module_init(init_walnut); ++module_exit(cleanup_walnut); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Heikki Lindholm "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405GP Walnut boards"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/maps/wr_sbc82xx_flash.c +@@ -0,0 +1,181 @@ ++/* ++ * $Id$ ++ * ++ * Map for flash chips on Wind River PowerQUICC II SBC82xx board. ++ * ++ * Copyright (C) 2004 Red Hat, Inc. ++ * ++ * Author: David Woodhouse ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static struct mtd_info *sbcmtd[3]; ++static struct mtd_partition *sbcmtd_parts[3]; ++ ++struct map_info sbc82xx_flash_map[3] = { ++ {.name = "Boot flash"}, ++ {.name = "Alternate boot flash"}, ++ {.name = "User flash"} ++}; ++ ++static struct mtd_partition smallflash_parts[] = { ++ { ++ .name = "space", ++ .size = 0x100000, ++ .offset = 0, ++ }, { ++ .name = "bootloader", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static struct mtd_partition bigflash_parts[] = { ++ { ++ .name = "bootloader", ++ .size = 0x00100000, ++ .offset = 0, ++ }, { ++ .name = "file system", ++ .size = 0x01f00000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "boot config", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "space", ++ .size = 0x01f00000, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; ++ ++#define init_sbc82xx_one_flash(map, br, or) \ ++do { \ ++ (map).phys = (br & 1) ? (br & 0xffff8000) : 0; \ ++ (map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0; \ ++ switch (br & 0x00001800) { \ ++ case 0x00000000: \ ++ case 0x00000800: (map).bankwidth = 1; break; \ ++ case 0x00001000: (map).bankwidth = 2; break; \ ++ case 0x00001800: (map).bankwidth = 4; break; \ ++ } \ ++} while (0); ++ ++int __init init_sbc82xx_flash(void) ++{ ++ volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; ++ int bigflash; ++ int i; ++ ++#ifdef CONFIG_SBC8560 ++ mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t)); ++#else ++ mc = &cpm2_immr->im_memctl; ++#endif ++ ++ bigflash = 1; ++ if ((mc->memc_br0 & 0x00001800) == 0x00001800) ++ bigflash = 0; ++ ++ init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0); ++ init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6); ++ init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1); ++ ++#ifdef CONFIG_SBC8560 ++ iounmap((void *) mc); ++#endif ++ ++ for (i=0; i<3; i++) { ++ int8_t flashcs[3] = { 0, 6, 1 }; ++ int nr_parts; ++ ++ printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d", ++ sbc82xx_flash_map[i].name, ++ (sbc82xx_flash_map[i].size >> 20), ++ flashcs[i]); ++ if (!sbc82xx_flash_map[i].phys) { ++ /* We know it can't be at zero. */ ++ printk("): disabled by bootloader.\n"); ++ continue; ++ } ++ printk(" at %08lx)\n", sbc82xx_flash_map[i].phys); ++ ++ sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); ++ ++ if (!sbc82xx_flash_map[i].virt) { ++ printk("Failed to ioremap\n"); ++ continue; ++ } ++ ++ simple_map_init(&sbc82xx_flash_map[i]); ++ ++ sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]); ++ ++ if (!sbcmtd[i]) ++ continue; ++ ++ sbcmtd[i]->owner = THIS_MODULE; ++ ++ nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes, ++ &sbcmtd_parts[i], 0); ++ if (nr_parts > 0) { ++ add_mtd_partitions (sbcmtd[i], sbcmtd_parts[i], nr_parts); ++ continue; ++ } ++ ++ /* No partitioning detected. Use default */ ++ if (i == 2) { ++ add_mtd_device(sbcmtd[i]); ++ } else if (i == bigflash) { ++ add_mtd_partitions (sbcmtd[i], bigflash_parts, ARRAY_SIZE(bigflash_parts)); ++ } else { ++ add_mtd_partitions (sbcmtd[i], smallflash_parts, ARRAY_SIZE(smallflash_parts)); ++ } ++ } ++ return 0; ++} ++ ++static void __exit cleanup_sbc82xx_flash(void) ++{ ++ int i; ++ ++ for (i=0; i<3; i++) { ++ if (!sbcmtd[i]) ++ continue; ++ ++ if (i<2 || sbcmtd_parts[i]) ++ del_mtd_partitions(sbcmtd[i]); ++ else ++ del_mtd_device(sbcmtd[i]); ++ ++ kfree(sbcmtd_parts[i]); ++ map_destroy(sbcmtd[i]); ++ ++ iounmap((void *)sbc82xx_flash_map[i].virt); ++ sbc82xx_flash_map[i].virt = 0; ++ } ++} ++ ++module_init(init_sbc82xx_flash); ++module_exit(cleanup_sbc82xx_flash); ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/mtd_blkdevs-24.c +@@ -0,0 +1,692 @@ ++/* ++ * $Id$ ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux 2.4 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ devfs_handle_t devfs_dir_handle; ++ int blksizes[256]; ++ int sizes[256]; ++ struct hd_struct part_table[256]; ++ struct gendisk gd; ++ spinlock_t devs_lock; /* See comment in _request function */ ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++}; ++ ++static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr, ++ int devnum) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_dev *d; ++ ++ list_for_each(this, &tr->devs) { ++ d = list_entry(this, struct mtd_blktrans_dev, list); ++ ++ if (d->devnum == devnum) ++ return d; ++ } ++ return NULL; ++} ++ ++static inline struct mtd_blktrans_ops *get_tr(int major) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_ops *t; ++ ++ list_for_each(this, &blktrans_majors) { ++ t = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ if (t->major == major) ++ return t; ++ } ++ return NULL; ++} ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ int minor; ++ ++ minor = MINOR(req->rq_dev); ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) { ++ printk(KERN_WARNING "Access beyond end of device.\n"); ++ return 0; ++ } ++ block += tr->blkcore_priv->part_table[minor].start_sect; ++ ++ switch(req->cmd) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major); ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ snprintf(current->comm, sizeof(current->comm), "%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ spin_lock_irq(¤t->sigmask_lock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sigmask_lock); ++ ++ daemonize("%sd", tr->name); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int devnum; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ spin_lock_irq(&io_request_lock); ++ ++ if (list_empty(&rq->queue_head)) { ++ ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(&io_request_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ continue; ++ } ++ ++ req = blkdev_entry_next_request(&rq->queue_head); ++ ++ devnum = MINOR(req->rq_dev) >> tr->part_bits; ++ ++ /* The ll_rw_blk code knows not to touch the request ++ at the head of the queue */ ++ spin_unlock_irq(&io_request_lock); ++ ++ /* FIXME: Where can we store the dev, on which ++ we already have a refcount anyway? We need to ++ lock against concurrent addition/removal of devices, ++ but if we use the mtd_table_mutex we deadlock when ++ grok_partitions is called from the registration ++ callbacks. */ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ dev = tr_get_dev(tr, devnum); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ BUG_ON(!dev); ++ ++ /* Ensure serialisation of requests */ ++ down(&dev->sem); ++ ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ if (!end_that_request_first(req, res, tr->name)) { ++ spin_lock_irq(&io_request_lock); ++ blkdev_dequeue_request(req); ++ end_that_request_last(req); ++ spin_unlock_irq(&io_request_lock); ++ } ++ } ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_ops *tr = NULL; ++ struct mtd_blktrans_dev *dev = NULL; ++ int major_nr = MAJOR(i->i_rdev); ++ int minor_nr = MINOR(i->i_rdev); ++ int devnum; ++ int ret = -ENODEV; ++ ++ if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE)) ++ return -EROFS; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(major_nr); ++ ++ if (!tr) ++ goto out; ++ ++ devnum = minor_nr >> tr->part_bits; ++ ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) ++ goto out; ++ ++ if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ if (!try_inc_mod_count(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_inc_mod_count(tr->owner)) ++ goto out_tr; ++ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ out_tr: ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ out: ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ int devnum; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(i->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(i->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev) ++{ ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int i; ++ int minor = MINOR(rdev); ++ ++ if (minor & ((1<part_bits)-1) || !tr->part_bits) { ++ /* BLKRRPART on a partition. Go away. */ ++ return -ENOTTY; ++ } ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ /* We are required to prevent simultaneous open() ourselves. ++ The core doesn't do that for us. Did I ever mention how ++ much the Linux block layer sucks? Sledgehammer approach... */ ++ down(&mtd_table_mutex); ++ ++ for (i=0; i < (1<part_bits); i++) { ++ invalidate_device(MKDEV(tr->major, minor+i), 1); ++ gd->part[minor + i].start_sect = 0; ++ gd->part[minor + i].nr_sects = 0; ++ } ++ ++ grok_partitions(gd, minor, 1 << tr->part_bits, ++ dev->size); ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int devnum; ++ ++ switch(cmd) { ++ case BLKGETSIZE: ++ case BLKGETSIZE64: ++ case BLKBSZSET: ++ case BLKBSZGET: ++ case BLKROSET: ++ case BLKROGET: ++ case BLKRASET: ++ case BLKRAGET: ++ case BLKPG: ++ case BLKELVGET: ++ case BLKELVSET: ++ return blk_ioctl(inode->i_rdev, cmd, arg); ++ } ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(inode->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(inode->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ up(&mtd_table_mutex); ++ ++ if (!dev) ++ return -ENODEV; ++ ++ switch(cmd) { ++ case BLKRRPART: ++ return mtd_blktrans_rrpart(inode->i_rdev, tr, dev); ++ ++ case BLKFLSBUF: ++ blk_ioctl(inode->i_rdev, cmd, arg); ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ if (ret) ++ return ret; ++ ++ g.start = gd->part[MINOR(inode->i_rdev)].start_sect; ++ if (copy_to_user((void *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ for (i = new->devnum << tr->part_bits; ++ i < (new->devnum+1) << tr->part_bits; ++ i++) { ++ set_device_ro(MKDEV(tr->major, i), new->readonly); ++ tr->blkcore_priv->blksizes[i] = new->blksize; ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ /* ++ dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices ++ dwmw2: any code which sets blk_size[][] should be ++ size >> 10 /+ 2.4 and its dumb units */ ++ ++ tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = ++ (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */ ++ ++ /* But this is still in device's sectors? $DEITY knows */ ++ tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size; ++ ++ if (tr->part_bits) { ++ grok_partitions(&tr->blkcore_priv->gd, new->devnum, ++ 1 << tr->part_bits, new->size); ++ } ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ char name[2]; ++ ++ name[0] = '0' + new->devnum; ++ name[1] = 0; ++ ++ new->blkcore_priv = ++ devfs_register(tr->blkcore_priv->devfs_dir_handle, ++ name, DEVFS_FL_DEFAULT, tr->major, ++ new->devnum, S_IFBLK|S_IRUGO|S_IWUGO, ++ &mtd_blktrans_ops, NULL); ++ } ++#endif ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ struct mtd_blktrans_ops *tr = old->tr; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ devfs_unregister(old->blkcore_priv); ++ old->blkcore_priv = NULL; ++ } else { ++ devfs_register_partitions(&tr->blkcore_priv->gd, ++ old->devnum << tr->part_bits, 1); ++ } ++#endif ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ list_del(&old->list); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ for (i = (old->devnum << tr->part_bits); ++ i < ((old->devnum+1) << tr->part_bits); i++) { ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ return 0; ++} ++ ++void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request); ++ (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr; ++ ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, ++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND); ++ if (ret < 0) { ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ tr->blkcore_priv->devfs_dir_handle = ++ devfs_mk_dir(NULL, tr->name, NULL); ++ ++ blksize_size[tr->major] = tr->blkcore_priv->blksizes; ++ blk_size[tr->major] = tr->blkcore_priv->sizes; ++ ++ tr->blkcore_priv->gd.major = tr->major; ++ tr->blkcore_priv->gd.major_name = tr->name; ++ tr->blkcore_priv->gd.minor_shift = tr->part_bits; ++ tr->blkcore_priv->gd.max_p = (1<part_bits) - 1; ++ tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table; ++ tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes; ++ tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits; ++ ++ spin_lock_init(&tr->blkcore_priv->devs_lock); ++ ++ add_gendisk(&tr->blkcore_priv->gd); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; itype != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ /* Remove each of its devices */ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ blksize_size[tr->major] = NULL; ++ blk_size[tr->major] = NULL; ++ ++ del_gendisk(&tr->blkcore_priv->gd); ++ ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ ++ devfs_unregister(tr->blkcore_priv->devfs_dir_handle); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/mtd_blkdevs.c +@@ -0,0 +1,478 @@ ++/* ++ * $Id$ ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux 2.5 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++ struct request_queue *rq; ++ spinlock_t queue_lock; ++}; ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (!(req->flags & REQ_CMD)) ++ return 0; ++ ++ if (block + nsect > get_capacity(req->rq_disk)) ++ return 0; ++ ++ switch(rq_data_dir(req)) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = tr->blkcore_priv->rq; ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC | PF_NOFREEZE; ++ ++ daemonize("%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ spin_lock_irq(¤t->sighand->siglock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ req = elv_next_request(rq); ++ ++ if (!req) { ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ continue; ++ } ++ ++ dev = req->rq_disk->private_data; ++ tr = dev->tr; ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ down(&dev->sem); ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ end_request(req, res); ++ } ++ spin_unlock_irq(rq->queue_lock); ++ ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++ ++static int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = -ENODEV; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (!try_module_get(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_module_get(tr->owner)) ++ goto out_tr; ++ ++ /* FIXME: Locking. A hot pluggable device can go away ++ (del_mtd_device can be called for it) without its module ++ being unloaded. */ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ out_tr: ++ module_put(tr->owner); ++ } ++ out: ++ return ret; ++} ++ ++static int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ module_put(tr->owner); ++ } ++ ++ return ret; ++} ++ ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; ++ struct mtd_blktrans_ops *tr = dev->tr; ++ ++ switch (cmd) { ++ case BLKFLSBUF: ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ if (ret) ++ return ret; ++ ++ g.start = get_start_sect(inode->i_bdev); ++ if (copy_to_user((void __user *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ struct gendisk *gd; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ gd = alloc_disk(1 << tr->part_bits); ++ if (!gd) { ++ list_del(&new->list); ++ return -ENOMEM; ++ } ++ gd->major = tr->major; ++ gd->first_minor = (new->devnum) << tr->part_bits; ++ gd->fops = &mtd_blktrans_ops; ++ ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ snprintf(gd->devfs_name, sizeof(gd->devfs_name), ++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ ++ /* 2.5 has capacity in units of 512 bytes while still ++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ ++ set_capacity(gd, (new->size * new->blksize) >> 9); ++ ++ gd->private_data = new; ++ new->blkcore_priv = gd; ++ gd->queue = tr->blkcore_priv->rq; ++ ++ if (new->readonly) ++ set_disk_ro(gd, 1); ++ ++ add_disk(gd); ++ ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_del(&old->list); ++ ++ del_gendisk(old->blkcore_priv); ++ put_disk(old->blkcore_priv); ++ ++ return 0; ++} ++ ++static void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++static void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = register_blkdev(tr->major, tr->name); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ spin_lock_init(&tr->blkcore_priv->queue_lock); ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); ++ if (!tr->blkcore_priv->rq) { ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return -ENOMEM; ++ } ++ ++ tr->blkcore_priv->rq->queuedata = tr; ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); ++ if (ret < 0) { ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ devfs_mk_dir(tr->name); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; itype != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ devfs_remove(tr->name); ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +--- linux-2.4.21/drivers/mtd/mtdblock.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdblock.c +@@ -1,52 +1,25 @@ + /* + * Direct MTD block device access + * +- * $Id$ ++ * $Id$ + * +- * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache ++ * (C) 2000-2003 Nicolas Pitre ++ * (C) 1999-2003 David Woodhouse + */ + + #include + #include + #include + #include ++#include ++#include + #include ++#include + #include +-#include +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include +-/* for old kernels... */ +-#ifndef QUEUE_EMPTY +-#define QUEUE_EMPTY (!CURRENT) +-#endif +-#if LINUX_VERSION_CODE < 0x20300 +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) +-#else +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) +-#endif +- +-#ifdef CONFIG_DEVFS_FS +-#include +-static void mtd_notify_add(struct mtd_info* mtd); +-static void mtd_notify_remove(struct mtd_info* mtd); +-static struct mtd_notifier notifier = { +- mtd_notify_add, +- mtd_notify_remove, +- NULL +-}; +-static devfs_handle_t devfs_dir_handle = NULL; +-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; +-#endif ++#include + + static struct mtdblk_dev { +- struct mtd_info *mtd; /* Locked */ ++ struct mtd_info *mtd; + int count; + struct semaphore cache_sem; + unsigned char *cache_data; +@@ -55,19 +28,6 @@ + enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; + } *mtdblks[MAX_MTD_DEVICES]; + +-static spinlock_t mtdblks_lock; +- +-static int mtd_sizes[MAX_MTD_DEVICES]; +-static int mtd_blksizes[MAX_MTD_DEVICES]; +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- + /* + * Cache stuff... + * +@@ -151,7 +111,7 @@ + return ret; + + /* +- * Here we could argably set the cache state to STATE_CLEAN. ++ * Here we could argubly set the cache state to STATE_CLEAN. + * However this could lead to inconsistency since we will not + * be notified if this content is altered on the flash by other + * means. Let's declare it empty and leave buffering tasks to +@@ -277,57 +237,47 @@ + return 0; + } + ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ return do_cached_read(mtdblk, block<<9, 512, buf); ++} + ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { ++ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); ++ if (!mtdblk->cache_data) ++ return -EINTR; ++ /* -EINTR is not really correct, but it is the best match ++ * documented in man 2 write for all cases. We could also ++ * return -EAGAIN sometimes, but why bother? ++ */ ++ } ++ return do_cached_write(mtdblk, block<<9, 512, buf); ++} + +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_open(struct mtd_blktrans_dev *mbd) + { + struct mtdblk_dev *mtdblk; +- struct mtd_info *mtd; +- int dev; ++ struct mtd_info *mtd = mbd->mtd; ++ int dev = mbd->devnum; + + DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); + +- if (!inode) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- if (dev >= MAX_MTD_DEVICES) +- return -EINVAL; +- +- BLK_INC_USE_COUNT; +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -ENODEV; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; +- return -ENODEV; +- } +- +- spin_lock(&mtdblks_lock); +- +- /* If it's already open, no need to piss about. */ + if (mtdblks[dev]) { + mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtd); + return 0; + } + +- /* OK, it's not open. Try to find it */ +- +- /* First we have to drop the lock, because we have to +- to things which might sleep. +- */ +- spin_unlock(&mtdblks_lock); +- ++ /* OK, it's not open. Create cache info for it */ + mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); +- if (!mtdblk) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; ++ if (!mtdblk) + return -ENOMEM; +- } ++ + memset(mtdblk, 0, sizeof(*mtdblk)); + mtdblk->count = 1; + mtdblk->mtd = mtd; +@@ -337,336 +287,102 @@ + if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && + mtdblk->mtd->erasesize) { + mtdblk->cache_size = mtdblk->mtd->erasesize; +- mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); +- if (!mtdblk->cache_data) { +- put_mtd_device(mtdblk->mtd); +- kfree(mtdblk); +- BLK_DEC_USE_COUNT; +- return -ENOMEM; +- } +- } +- +- /* OK, we've created a new one. Add it to the list. */ +- +- spin_lock(&mtdblks_lock); +- +- if (mtdblks[dev]) { +- /* Another CPU made one at the same time as us. */ +- mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtdblk->mtd); +- vfree(mtdblk->cache_data); +- kfree(mtdblk); +- return 0; ++ mtdblk->cache_data = NULL; + } + + mtdblks[dev] = mtdblk; +- mtd_sizes[dev] = mtdblk->mtd->size/1024; +- if (mtdblk->mtd->erasesize) +- mtd_blksizes[dev] = mtdblk->mtd->erasesize; +- if (mtd_blksizes[dev] > PAGE_SIZE) +- mtd_blksizes[dev] = PAGE_SIZE; +- set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); +- +- spin_unlock(&mtdblks_lock); + + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_release(struct mtd_blktrans_dev *mbd) + { +- int dev; +- struct mtdblk_dev *mtdblk; +- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); +- +- if (inode == NULL) +- release_return(-ENODEV); ++ int dev = mbd->devnum; ++ struct mtdblk_dev *mtdblk = mtdblks[dev]; + +- dev = MINOR(inode->i_rdev); +- mtdblk = mtdblks[dev]; ++ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); + + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); + +- spin_lock(&mtdblks_lock); + if (!--mtdblk->count) { + /* It was the last usage. Free the device */ + mtdblks[dev] = NULL; +- spin_unlock(&mtdblks_lock); + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); +- put_mtd_device(mtdblk->mtd); + vfree(mtdblk->cache_data); + kfree(mtdblk); +- } else { +- spin_unlock(&mtdblks_lock); + } +- + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + +- BLK_DEC_USE_COUNT; +- release_return(0); +-} +- +- +-/* +- * This is a special request_fn because it is executed in a process context +- * to be able to sleep independently of the caller. The io_request_lock +- * is held upon entry and exit. +- * The head of our request queue is considered active so there is no need +- * to dequeue requests before we are done. +- */ +-static void handle_mtdblock_request(void) +-{ +- struct request *req; +- struct mtdblk_dev *mtdblk; +- unsigned int res; +- +- for (;;) { +- INIT_REQUEST; +- req = CURRENT; +- spin_unlock_irq(&io_request_lock); +- mtdblk = mtdblks[MINOR(req->rq_dev)]; +- res = 0; +- +- if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) +- panic("%s: minor out of bounds", __FUNCTION__); +- +- if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) +- goto end_req; +- +- // Handle the request +- switch (req->cmd) +- { +- int err; +- +- case READ: +- down(&mtdblk->cache_sem); +- err = do_cached_read (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- +- case WRITE: +- // Read only device +- if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) +- break; +- +- // Do the write +- down(&mtdblk->cache_sem); +- err = do_cached_write (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- } +- +-end_req: +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static volatile int leaving = 0; +-static DECLARE_MUTEX_LOCKED(thread_sem); +-static DECLARE_WAIT_QUEUE_HEAD(thr_wq); +- +-int mtdblock_thread(void *dummy) +-{ +- struct task_struct *tsk = current; +- DECLARE_WAITQUEUE(wait, tsk); +- +- /* we might get involved when memory gets low, so use PF_MEMALLOC */ +- tsk->flags |= PF_MEMALLOC; +- strcpy(tsk->comm, "mtdblockd"); +- spin_lock_irq(&tsk->sigmask_lock); +- sigfillset(&tsk->blocked); +- recalc_sigpending(tsk); +- spin_unlock_irq(&tsk->sigmask_lock); +- daemonize(); +- +- while (!leaving) { +- add_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- spin_lock_irq(&io_request_lock); +- if (QUEUE_EMPTY || QUEUE_PLUGGED) { +- spin_unlock_irq(&io_request_lock); +- schedule(); +- remove_wait_queue(&thr_wq, &wait); +- } else { +- remove_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_RUNNING); +- handle_mtdblock_request(); +- spin_unlock_irq(&io_request_lock); +- } +- } +- +- up(&thread_sem); + return 0; + } + +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-static void mtdblock_request(RQFUNC_ARG) +-{ +- /* Don't do anything, except wake the thread if necessary */ +- wake_up(&thr_wq); +-} +- +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) ++static int mtdblock_flush(struct mtd_blktrans_dev *dev) + { +- struct mtdblk_dev *mtdblk; +- +- mtdblk = mtdblks[MINOR(inode->i_rdev)]; +- +-#ifdef PARANOIA +- if (!mtdblk) +- BUG(); +-#endif +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtdblk->mtd->size, (u64 *)arg); +-#endif ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) +- return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); ++ + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); + return 0; +- +- default: +- return -EINVAL; +- } + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = +-{ +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl +-}; +-#endif +- +-#ifdef CONFIG_DEVFS_FS +-/* Notification that a new device has been added. Create the devfs entry for +- * it. */ +- +-static void mtd_notify_add(struct mtd_info* mtd) ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- char name[8]; ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +- if (!mtd || mtd->type == MTD_ABSENT) ++ if (!dev) + return; + +- sprintf(name, "%d", mtd->index); +- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, +- DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, +- S_IFBLK | S_IRUGO | S_IWUGO, +- &mtd_fops, NULL); +-} ++ memset(dev, 0, sizeof(*dev)); + +-static void mtd_notify_remove(struct mtd_info* mtd) +-{ +- if (!mtd || mtd->type == MTD_ABSENT) +- return; ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; + +- devfs_unregister(devfs_rw_handle[mtd->index]); ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ dev->readonly = 1; ++ ++ add_mtd_blktrans_dev(dev); + } +-#endif + +-int __init init_mtdblock(void) ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i; +- +- spin_lock_init(&mtdblks_lock); +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); +- register_mtd_user(¬ifier); +-#else +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +-#endif ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); ++} + +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- mtd_blksizes[i] = BLOCK_SIZE; +- } +- init_waitqueue_head(&thr_wq); +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = mtd_blksizes; +- blk_size[MAJOR_NR] = mtd_sizes; ++static struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .open = mtdblock_open, ++ .flush = mtdblock_flush, ++ .release = mtdblock_release, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, ++}; + +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); +- return 0; ++static int __init init_mtdblock(void) ++{ ++ return register_mtd_blktrans(&mtdblock_tr); + } + + static void __exit cleanup_mtdblock(void) + { +- leaving = 1; +- wake_up(&thr_wq); +- down(&thread_sem); +-#ifdef CONFIG_DEVFS_FS +- unregister_mtd_user(¬ifier); +- devfs_unregister(devfs_dir_handle); +- devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); +-#else +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +-#endif +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = NULL; ++ deregister_mtd_blktrans(&mtdblock_tr); + } + + module_init(init_mtdblock); +--- linux-2.4.21/drivers/mtd/mtdblock_ro.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdblock_ro.c +@@ -1,301 +1,87 @@ + /* +- * $Id$ ++ * $Id$ + * +- * Read-only version of the mtdblock device, without the +- * read/erase/modify/writeback stuff ++ * (C) 2003 David Woodhouse ++ * ++ * Simple read-only (writable only for RAM) mtdblock driver + */ + +-#ifdef MTDBLOCK_DEBUG +-#define DEBUGLVL debug +-#endif +- +- +-#include +-#include +- ++#include ++#include + #include +-#include +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include +- +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-#ifdef MTDBLOCK_DEBUG +-static int debug = MTDBLOCK_DEBUG; +-MODULE_PARM(debug, "i"); +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- +-static int mtd_sizes[MAX_MTD_DEVICES]; +- ++#include + +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- struct mtd_info *mtd = NULL; +- +- int dev; +- +- DEBUG(1,"mtdblock_open\n"); +- +- if (inode == 0) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -EINVAL; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- return -EINVAL; +- } +- +- BLK_INC_USE_COUNT; +- +- mtd_sizes[dev] = mtd->size>>9; +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + ++ if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int dev; +- struct mtd_info *mtd; +- +- DEBUG(1, "mtdblock_release\n"); +- +- if (inode == NULL) +- release_return(-ENODEV); +- +- dev = MINOR(inode->i_rdev); +- mtd = __get_mtd_device(NULL, dev); +- +- if (!mtd) { +- printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); +- BLK_DEC_USE_COUNT; +- release_return(-ENODEV); +- } +- +- if (mtd->sync) +- mtd->sync(mtd); +- +- put_mtd_device(mtd); +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + +- BLK_DEC_USE_COUNT; +- release_return(0); ++ if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; ++ return 0; + } + +- +-static void mtdblock_request(RQFUNC_ARG) ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- struct request *current_request; +- unsigned int res = 0; +- struct mtd_info *mtd; +- +- while (1) +- { +- /* Grab the Request and unlink it from the request list, INIT_REQUEST +- will execute a return if we are done. */ +- INIT_REQUEST; +- current_request = CURRENT; +- +- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) +- { +- printk("mtd: Unsupported device!\n"); +- end_request(0); +- continue; +- } +- +- // Grab our MTD structure +- +- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); +- if (!mtd) { +- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); +- end_request(0); +- } +- +- if (current_request->sector << 9 > mtd->size || +- (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size) +- { +- printk("mtd: Attempt to read past end of device!\n"); +- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, +- current_request->sector, current_request->current_nr_sectors); +- end_request(0); +- continue; +- } +- +- /* Remove the request we are handling from the request list so nobody messes +- with it */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- /* Now drop the lock that the ll_rw_blk functions grabbed for us +- and process the request. This is necessary due to the extreme time +- we spend processing it. */ +- spin_unlock_irq(&io_request_lock); +-#endif +- +- // Handle the request +- switch (current_request->cmd) +- { +- size_t retlen; +- +- case READ: +- if (MTD_READ(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; +- +- case WRITE: +- +- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, +- current_request->current_nr_sectors); +- */ ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +- // Read only device +- if ((mtd->flags & MTD_CAP_RAM) == 0) +- { +- res = 0; +- break; +- } ++ if (!dev) ++ return; + +- // Do the write +- if (MTD_WRITE(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; ++ memset(dev, 0, sizeof(*dev)); + +- // Shouldn't happen +- default: +- printk("mtd: unknown request\n"); +- break; +- } ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; ++ if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) != ++ (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) ++ dev->readonly = 1; + +- // Grab the lock and re-thread the item onto the linked list +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- spin_lock_irq(&io_request_lock); +-#endif +- end_request(res); +- } ++ add_mtd_blktrans_dev(dev); + } + +- +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) + { +- struct mtd_info *mtd; +- +- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); +- +- if (!mtd) return -EINVAL; +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtd->size, (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (mtd->sync) +- mtd->sync(mtd); +- return 0; +- +- default: +- return -ENOTTY; +- } ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = +-{ +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl ++static struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, + }; +-#endif + +-int __init init_mtdblock(void) ++static int __init mtdblock_init(void) + { +- int i; +- +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- } +- +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = mtd_sizes; +- +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- return 0; ++ return register_mtd_blktrans(&mtdblock_tr); + } + +-static void __exit cleanup_mtdblock(void) ++static void __exit mtdblock_exit(void) + { +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +- blk_size[MAJOR_NR] = NULL; +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); ++ deregister_mtd_blktrans(&mtdblock_tr); + } + +-module_init(init_mtdblock); +-module_exit(cleanup_mtdblock); +- ++module_init(mtdblock_init); ++module_exit(mtdblock_exit); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Erwin Authried et al."); ++MODULE_AUTHOR("David Woodhouse "); + MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices"); +--- linux-2.4.21/drivers/mtd/mtdchar.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdchar.c +@@ -1,8 +1,7 @@ + /* +- * $Id$ ++ * $Id$ + * + * Character-device access to raw MTD devices. +- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c + * + */ + +@@ -10,10 +9,15 @@ + #include + #include + #include ++#include + #include ++#include ++#include ++#include + + #ifdef CONFIG_DEVFS_FS + #include ++#ifndef NEW + static void mtd_notify_add(struct mtd_info* mtd); + static void mtd_notify_remove(struct mtd_info* mtd); + +@@ -27,9 +31,98 @@ + static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES]; + #endif + ++static void mtd_notify_add(struct mtd_info* mtd) ++{ ++#ifndef NEW ++ char name[8]; ++#endif ++ if (!mtd) ++ return; ++ ++#ifdef NEW ++ devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), ++ S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); ++ ++ devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), ++ S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); ++#else ++ struct file_operations mtd_fops; ++ ++ sprintf(name, "%d", mtd->index); ++ devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, ++ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, ++ S_IFCHR | S_IRUGO | S_IWUGO, ++ &mtd_fops, NULL); ++ ++ sprintf(name, "%dro", mtd->index); ++ devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, ++ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, ++ S_IFCHR | S_IRUGO, ++ &mtd_fops, NULL); ++#endif ++} ++ ++static void mtd_notify_remove(struct mtd_info* mtd) ++{ ++ if (!mtd) ++ return; ++#ifdef NEW ++ devfs_remove("mtd/%d", mtd->index); ++ devfs_remove("mtd/%dro", mtd->index); ++#else ++ devfs_unregister(devfs_rw_handle[mtd->index]); ++ devfs_unregister(devfs_ro_handle[mtd->index]); ++#endif ++} ++ ++#ifdef NEW ++static struct mtd_notifier notifier = { ++ .add = mtd_notify_add, ++ .remove = mtd_notify_remove, ++}; ++#endif ++ ++static inline void mtdchar_devfs_init(void) ++{ ++#ifdef NEW ++ devfs_mk_dir("mtd"); ++ register_mtd_user(¬ifier); ++#else ++ devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); ++ ++ register_mtd_user(¬ifier); ++#endif ++} ++ ++static inline void mtdchar_devfs_exit(void) ++{ ++ unregister_mtd_user(¬ifier); ++ devfs_remove("mtd"); ++} ++#else /* !DEVFS */ ++#define mtdchar_devfs_init() do { } while(0) ++#define mtdchar_devfs_exit() do { } while(0) ++#endif ++ ++/* ++ * We use file->private_data to store a pointer to the MTDdevice. ++ * Since alighment is at least 32 bits, we have 2 bits free for OTP ++ * modes as well. ++ */ ++ ++#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) ++ ++#define MTD_MODE_OTP_FACT 1 ++#define MTD_MODE_OTP_USER 2 ++#define MTD_MODE(file) ((long)((file)->private_data) & 3) ++ ++#define SET_MTD_MODE(file, mode) \ ++ do { long __p = (long)((file)->private_data); \ ++ (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) ++ + static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) + { +- struct mtd_info *mtd=(struct mtd_info *)file->private_data; ++ struct mtd_info *mtd = TO_MTD(file); + + switch (orig) { + case 0: +@@ -60,7 +153,7 @@ + + static int mtd_open(struct inode *inode, struct file *file) + { +- int minor = minor(inode->i_rdev); ++ int minor = iminor(inode); + int devnum = minor >> 1; + struct mtd_info *mtd; + +@@ -102,7 +195,7 @@ + + DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); + +- mtd = (struct mtd_info *)file->private_data; ++ mtd = TO_MTD(file); + + if (mtd->sync) + mtd->sync(mtd); +@@ -117,9 +210,9 @@ + */ + #define MAX_KMALLOC_SIZE 0x20000 + +-static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) ++static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) + { +- struct mtd_info *mtd = (struct mtd_info *)file->private_data; ++ struct mtd_info *mtd = TO_MTD(file); + size_t retlen=0; + size_t total_retlen=0; + int ret=0; +@@ -146,8 +239,23 @@ + if (!kbuf) + return -ENOMEM; + ++ switch (MTD_MODE(file)) { ++ case MTD_MODE_OTP_FACT: ++ ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ case MTD_MODE_OTP_USER: ++ ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ default: + ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); +- if (!ret) { ++ } ++ /* Nand returns -EBADMSG on ecc errors, but it returns ++ * the data. For our userspace tools it is important ++ * to dump areas with ecc errors ! ++ * Userspace software which accesses NAND this way ++ * must be aware of the fact that it deals with NAND ++ */ ++ if (!ret || (ret == -EBADMSG)) { + *ppos += retlen; + if (copy_to_user(buf, kbuf, retlen)) { + kfree(kbuf); +@@ -158,6 +266,8 @@ + + count -= retlen; + buf += retlen; ++ if (retlen == 0) ++ count = 0; + } + else { + kfree(kbuf); +@@ -170,9 +280,9 @@ + return total_retlen; + } /* mtd_read */ + +-static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos) ++static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) + { +- struct mtd_info *mtd = (struct mtd_info *)file->private_data; ++ struct mtd_info *mtd = TO_MTD(file); + char *kbuf; + size_t retlen; + size_t total_retlen=0; +@@ -207,7 +317,20 @@ + return -EFAULT; + } + ++ switch (MTD_MODE(file)) { ++ case MTD_MODE_OTP_FACT: ++ ret = -EROFS; ++ break; ++ case MTD_MODE_OTP_USER: ++ if (!mtd->write_user_prot_reg) { ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ default: + ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); ++ } + if (!ret) { + *ppos += retlen; + total_retlen += retlen; +@@ -230,7 +353,7 @@ + IOCTL calls for getting device parameters. + + ======================================================================*/ +-static void mtd_erase_callback (struct erase_info *instr) ++static void mtdchar_erase_callback (struct erase_info *instr) + { + wake_up((wait_queue_head_t *)instr->priv); + } +@@ -238,7 +361,8 @@ + static int mtd_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) + { +- struct mtd_info *mtd = (struct mtd_info *)file->private_data; ++ struct mtd_info *mtd = TO_MTD(file); ++ void __user *argp = (void __user *)arg; + int ret = 0; + u_long size; + +@@ -246,17 +370,17 @@ + + size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; + if (cmd & IOC_IN) { +- ret = verify_area(VERIFY_READ, (char *)arg, size); ++ ret = verify_area(VERIFY_READ, argp, size); + if (ret) return ret; + } + if (cmd & IOC_OUT) { +- ret = verify_area(VERIFY_WRITE, (char *)arg, size); ++ ret = verify_area(VERIFY_WRITE, argp, size); + if (ret) return ret; + } + + switch (cmd) { + case MEMGETREGIONCOUNT: +- if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int))) ++ if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) + return -EFAULT; + break; + +@@ -264,24 +388,19 @@ + { + struct region_info_user ur; + +- if (copy_from_user( &ur, +- (struct region_info_user *)arg, +- sizeof(struct region_info_user))) { ++ if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) + return -EFAULT; +- } + + if (ur.regionindex >= mtd->numeraseregions) + return -EINVAL; +- if (copy_to_user((struct mtd_erase_region_info *) arg, +- &(mtd->eraseregions[ur.regionindex]), ++ if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), + sizeof(struct mtd_erase_region_info))) + return -EFAULT; + break; + } + + case MEMGETINFO: +- if (copy_to_user((struct mtd_info *)arg, mtd, +- sizeof(struct mtd_info_user))) ++ if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user))) + return -EFAULT; + break; + +@@ -302,13 +421,13 @@ + init_waitqueue_head(&waitq); + + memset (erase,0,sizeof(struct erase_info)); +- if (copy_from_user(&erase->addr, (u_long *)arg, +- 2 * sizeof(u_long))) { ++ if (copy_from_user(&erase->addr, argp, ++ sizeof(struct erase_info_user))) { + kfree(erase); + return -EFAULT; + } + erase->mtd = mtd; +- erase->callback = mtd_erase_callback; ++ erase->callback = mtdchar_erase_callback; + erase->priv = (unsigned long)&waitq; + + /* +@@ -346,7 +465,7 @@ + if(!(file->f_mode & 2)) + return -EPERM; + +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + + if (buf.length > 0x4096) +@@ -355,7 +474,7 @@ + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else +- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); ++ ret = verify_area(VERIFY_READ, buf.ptr, buf.length); + + if (ret) + return ret; +@@ -371,7 +490,7 @@ + + ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); + +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ++ if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) + ret = -EFAULT; + + kfree(databuf); +@@ -385,7 +504,7 @@ + void *databuf; + ssize_t retlen; + +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + + if (buf.length > 0x4096) +@@ -394,7 +513,7 @@ + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else +- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); ++ ret = verify_area(VERIFY_WRITE, buf.ptr, buf.length); + + if (ret) + return ret; +@@ -405,7 +524,7 @@ + + ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); + +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ++ if (put_user(retlen, (uint32_t __user *)argp)) + ret = -EFAULT; + else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) + ret = -EFAULT; +@@ -416,109 +535,146 @@ + + case MEMLOCK: + { +- unsigned long adrs[2]; ++ struct erase_info_user info; + +- if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long))) ++ if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + + if (!mtd->lock) + ret = -EOPNOTSUPP; + else +- ret = mtd->lock(mtd, adrs[0], adrs[1]); ++ ret = mtd->lock(mtd, info.start, info.length); + break; + } + + case MEMUNLOCK: + { +- unsigned long adrs[2]; ++ struct erase_info_user info; + +- if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long))) ++ if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + + if (!mtd->unlock) + ret = -EOPNOTSUPP; + else +- ret = mtd->unlock(mtd, adrs[0], adrs[1]); ++ ret = mtd->unlock(mtd, info.start, info.length); + break; + } + +- case MEMWRITEDATA: ++ case MEMSETOOBSEL: + { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen; ++ if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) ++ return -EFAULT; ++ break; ++ } + +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ case MEMGETOOBSEL: ++ { ++ if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) + return -EFAULT; ++ break; ++ } + +- if (buf.length > 0x4096) +- return -EINVAL; ++ case MEMGETBADBLOCK: ++ { ++ loff_t offs; + +- if (!mtd->write_ecc) ++ if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ return -EFAULT; ++ if (!mtd->block_isbad) + ret = -EOPNOTSUPP; + else +- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; +- +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) +- return -ENOMEM; +- +- if (copy_from_user(databuf, buf.ptr, buf.length)) { +- kfree(databuf); +- return -EFAULT; ++ return mtd->block_isbad(mtd, offs); ++ break; + } + +- ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; ++ case MEMSETBADBLOCK: ++ { ++ loff_t offs; + +- kfree(databuf); ++ if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ return -EFAULT; ++ if (!mtd->block_markbad) ++ ret = -EOPNOTSUPP; ++ else ++ return mtd->block_markbad(mtd, offs); + break; +- + } + +- case MEMREADDATA: ++#ifdef CONFIG_MTD_OTP ++ case OTPSELECT: + { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen = 0; +- +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ int mode; ++ if (copy_from_user(&mode, argp, sizeof(int))) + return -EFAULT; +- +- if (buf.length > 0x4096) +- return -EINVAL; +- +- if (!mtd->read_ecc) ++ SET_MTD_MODE(file, 0); ++ switch (mode) { ++ case MTD_OTP_FACTORY: ++ if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else +- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; ++ SET_MTD_MODE(file, MTD_MODE_OTP_FACT); ++ break; ++ case MTD_OTP_USER: ++ if (!mtd->read_fact_prot_reg) ++ ret = -EOPNOTSUPP; ++ else ++ SET_MTD_MODE(file, MTD_MODE_OTP_USER); ++ break; ++ default: ++ ret = -EINVAL; ++ case MTD_OTP_OFF: ++ break; ++ } ++ break; ++ } + +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) ++ case OTPGETREGIONCOUNT: ++ case OTPGETREGIONINFO: ++ { ++ struct otp_info *buf = kmalloc(4096, GFP_KERNEL); ++ if (!buf) + return -ENOMEM; +- +- ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; +- else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) ++ ret = -EOPNOTSUPP; ++ switch (MTD_MODE(file)) { ++ case MTD_MODE_OTP_FACT: ++ if (mtd->get_fact_prot_info) ++ ret = mtd->get_fact_prot_info(mtd, buf, 4096); ++ break; ++ case MTD_MODE_OTP_USER: ++ if (mtd->get_user_prot_info) ++ ret = mtd->get_user_prot_info(mtd, buf, 4096); ++ break; ++ } ++ if (ret >= 0) { ++ if (cmd == OTPGETREGIONCOUNT) { ++ int nbr = ret / sizeof(struct otp_info); ++ ret = copy_to_user(argp, &nbr, sizeof(int)); ++ } else ++ ret = copy_to_user(argp, buf, ret); ++ if (ret) + ret = -EFAULT; +- +- kfree(databuf); ++ } ++ kfree(buf); + break; + } + ++ case OTPLOCK: ++ { ++ struct otp_info info; ++ ++ if (MTD_MODE(file) != MTD_MODE_OTP_USER) ++ return -EINVAL; ++ if (copy_from_user(&info, argp, sizeof(info))) ++ return -EFAULT; ++ if (!mtd->lock_user_prot_reg) ++ return -EOPNOTSUPP; ++ ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); ++ break; ++ } ++#endif + + default: +- DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); + ret = -ENOTTY; + } + +@@ -526,84 +682,31 @@ + } /* memory_ioctl */ + + static struct file_operations mtd_fops = { +- owner: THIS_MODULE, +- llseek: mtd_lseek, /* lseek */ +- read: mtd_read, /* read */ +- write: mtd_write, /* write */ +- ioctl: mtd_ioctl, /* ioctl */ +- open: mtd_open, /* open */ +- release: mtd_close, /* release */ ++ .owner = THIS_MODULE, ++ .llseek = mtd_lseek, ++ .read = mtd_read, ++ .write = mtd_write, ++ .ioctl = mtd_ioctl, ++ .open = mtd_open, ++ .release = mtd_close, + }; + +- +-#ifdef CONFIG_DEVFS_FS +-/* Notification that a new device has been added. Create the devfs entry for +- * it. */ +- +-static void mtd_notify_add(struct mtd_info* mtd) +-{ +- char name[8]; +- +- if (!mtd) +- return; +- +- sprintf(name, "%d", mtd->index); +- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, +- DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, +- S_IFCHR | S_IRUGO | S_IWUGO, +- &mtd_fops, NULL); +- +- sprintf(name, "%dro", mtd->index); +- devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, +- DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, +- S_IFCHR | S_IRUGO, +- &mtd_fops, NULL); +-} +- +-static void mtd_notify_remove(struct mtd_info* mtd) +-{ +- if (!mtd) +- return; +- +- devfs_unregister(devfs_rw_handle[mtd->index]); +- devfs_unregister(devfs_ro_handle[mtd->index]); +-} +-#endif +- + static int __init init_mtdchar(void) + { +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_CHAR_MAJOR); +- return -EAGAIN; +- } +- +- devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); +- +- register_mtd_user(¬ifier); +-#else +- if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) +- { ++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return -EAGAIN; + } +-#endif + ++ mtdchar_devfs_init(); + return 0; + } + + static void __exit cleanup_mtdchar(void) + { +-#ifdef CONFIG_DEVFS_FS +- unregister_mtd_user(¬ifier); +- devfs_unregister(devfs_dir_handle); +- devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +-#else ++ mtdchar_devfs_exit(); + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +-#endif + } + + module_init(init_mtdchar); +--- linux-2.4.21/drivers/mtd/mtdconcat.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdconcat.c +@@ -3,9 +3,11 @@ + * + * (C) 2002 Robert Kaiser + * ++ * NAND support by Christian Gan ++ * + * This code is GPL + * +- * $Id$ ++ * $Id$ + */ + + #include +@@ -35,21 +37,20 @@ + #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +- + /* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ + #define CONCAT(x) ((struct mtd_concat *)(x)) + +- + /* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf) ++static int ++concat_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -57,43 +58,43 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (from >= subdev->size) +- { ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ + size = 0; + from -= subdev->size; ++ continue; + } +- else +- { + if (from + len > subdev->size) ++ /* First part goes into this subdev */ + size = subdev->size - from; + else ++ /* Entire transaction goes into this subdev */ + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } +- } + return err; + } + +-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf) ++static int ++concat_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -104,18 +105,15 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (to >= subdev->size) +- { ++ if (to >= subdev->size) { + size = 0; + to -= subdev->size; ++ continue; + } +- else +- { + if (to + len > subdev->size) + size = subdev->size - to; + else +@@ -126,25 +124,232 @@ + else + err = subdev->write(subdev, to, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } ++ return err; ++} ++ ++static int ++concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_ecc) ++ err = subdev->read_ecc(subdev, from, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) { ++ eccbuf += subdev->oobsize; ++ /* in nand.c at least, eccbufs are ++ tagged with 2 (int)eccstatus'; we ++ must account for these */ ++ eccbuf += 2 * (sizeof (int)); ++ } ++ from = 0; + } + return err; + } + +-static void concat_erase_callback (struct erase_info *instr) ++static int ++concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) + { +- wake_up((wait_queue_head_t *)instr->priv); ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_ecc) ++ err = subdev->write_ecc(subdev, to, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) ++ eccbuf += subdev->oobsize; ++ to = 0; ++ } ++ return err; ++} ++ ++static int ++concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_oob) ++ err = subdev->read_oob(subdev, from, size, ++ &retsize, buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ from = 0; ++ } ++ return err; ++} ++ ++static int ++concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_oob) ++ err = subdev->write_oob(subdev, to, size, &retsize, ++ buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ to = 0; ++ } ++ return err; ++} ++ ++static void concat_erase_callback(struct erase_info *instr) ++{ ++ wake_up((wait_queue_head_t *) instr->priv); + } + + static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +@@ -160,18 +365,18 @@ + + erase->mtd = mtd; + erase->callback = concat_erase_callback; +- erase->priv = (unsigned long)&waitq; ++ erase->priv = (unsigned long) &waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); +- if (!err) +- { ++ if (!err) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); +- if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) ++ if (erase->state != MTD_ERASE_DONE ++ && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); +@@ -181,21 +386,21 @@ + return err; + } + +-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ++static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) + { + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; +- u_int32_t length; ++ u_int32_t length, offset = 0; + struct erase_info *erase; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + +- if(instr->addr > concat->mtd.size) ++ if (instr->addr > concat->mtd.size) + return -EINVAL; + +- if(instr->len + instr->addr > concat->mtd.size) ++ if (instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* +@@ -204,23 +409,22 @@ + * region info rather than looking at each particular sub-device + * in turn. + */ +- if (!concat->mtd.numeraseregions) +- { /* the easy case: device has uniform erase block size */ +- if(instr->addr & (concat->mtd.erasesize - 1)) ++ if (!concat->mtd.numeraseregions) { ++ /* the easy case: device has uniform erase block size */ ++ if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; +- if(instr->len & (concat->mtd.erasesize - 1)) ++ if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; +- } +- else +- { /* device has variable erase size */ +- struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; ++ } else { ++ /* device has variable erase size */ ++ struct mtd_erase_region_info *erase_regions = ++ concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ +- for(i = 0; i < concat->mtd.numeraseregions && +- instr->addr >= erase_regions[i].offset; i++) +- ; ++ for (i = 0; i < concat->mtd.numeraseregions && ++ instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* +@@ -228,25 +432,28 @@ + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ +- if (instr->addr & (erase_regions[i].erasesize-1)) ++ if (instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ +- for(; i < concat->mtd.numeraseregions && +- (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) +- ; ++ for (; i < concat->mtd.numeraseregions && ++ (instr->addr + instr->len) >= erase_regions[i].offset; ++ ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ +- if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) ++ if ((instr->addr + instr->len) & (erase_regions[i].erasesize - ++ 1)) + return -EINVAL; + } + ++ instr->fail_addr = 0xffffffff; ++ + /* make a local copy of instr to avoid modifying the caller's struct */ +- erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); ++ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; +@@ -258,39 +465,44 @@ + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; +- if(subdev->size <= erase->addr) ++ if (subdev->size <= erase->addr) { + erase->addr -= subdev->size; +- else ++ offset += subdev->size; ++ } else { + break; + } +- if(i >= concat->num_subdev) /* must never happen since size */ +- BUG(); /* limit has been verified above */ ++ } ++ ++ /* must never happen since size limit has been verified above */ ++ if (i >= concat->num_subdev) ++ BUG(); + + /* now do the erase: */ + err = 0; +- for(;length > 0; i++) /* loop for all subevices affected by this request */ +- { ++ for (; length > 0; i++) { ++ /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ +- if(erase->addr + length > subdev->size) ++ if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + +- if (!(subdev->flags & MTD_WRITEABLE)) +- { ++ if (!(subdev->flags & MTD_WRITEABLE)) { + err = -EROFS; + break; + } + length -= erase->len; +- if ((err = concat_dev_erase(subdev, erase))) +- { +- if(err == -EINVAL) /* sanity check: must never happen since */ +- BUG(); /* block alignment has been checked above */ ++ if ((err = concat_dev_erase(subdev, erase))) { ++ /* sanity check: should never happen since ++ * block alignment has been checked above */ ++ if (err == -EINVAL) ++ BUG(); ++ if (erase->fail_addr != 0xffffffff) ++ instr->fail_addr = erase->fail_addr + offset; + break; + } + /* +@@ -302,18 +514,19 @@ + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; ++ offset += subdev->size; + } ++ instr->state = erase->state; + kfree(erase); + if (err) + return err; + +- instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + return 0; + } + +-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; +@@ -321,18 +534,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -340,21 +550,21 @@ + + err = subdev->lock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; +@@ -362,18 +572,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -381,17 +588,17 @@ + + err = subdev->unlock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +@@ -400,8 +607,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +@@ -412,10 +618,9 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; +- if((rc = subdev->suspend(subdev)) < 0) ++ if ((rc = subdev->suspend(subdev)) < 0) + return rc; + } + return rc; +@@ -426,8 +631,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->resume(subdev); + } +@@ -439,11 +643,10 @@ + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +-struct mtd_info *mtd_concat_create( +- struct mtd_info *subdev[], /* subdevices to concatenate */ ++struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ +- char *name) /* name for the new device */ +-{ ++ char *name) ++{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; +@@ -451,21 +654,21 @@ + int num_erase_region; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); +- for(i = 0; i < num_devs; i++) ++ for (i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); +- concat = kmalloc (size, GFP_KERNEL); +- if(!concat) +- { +- printk ("memory allocation error while creating concatenated device \"%s\"\n", ++ concat = kmalloc(size, GFP_KERNEL); ++ if (!concat) { ++ printk ++ ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + memset(concat, 0, size); +- concat->subdev = (struct mtd_info **)(concat + 1); ++ concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for +@@ -479,39 +682,53 @@ + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; ++ if (subdev[0]->read_ecc) ++ concat->mtd.read_ecc = concat_read_ecc; ++ if (subdev[0]->write_ecc) ++ concat->mtd.write_ecc = concat_write_ecc; ++ if (subdev[0]->read_oob) ++ concat->mtd.read_oob = concat_read_oob; ++ if (subdev[0]->write_oob) ++ concat->mtd.write_oob = concat_write_oob; + + concat->subdev[0] = subdev[0]; + +- for(i = 1; i < num_devs; i++) +- { +- if(concat->mtd.type != subdev[i]->type) +- { ++ for (i = 1; i < num_devs; i++) { ++ if (concat->mtd.type != subdev[i]->type) { + kfree(concat); +- printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device type on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } +- if(concat->mtd.flags != subdev[i]->flags) +- { /* +- * Expect all flags except MTD_WRITEABLE to be equal on +- * all subdevices. ++ if (concat->mtd.flags != subdev[i]->flags) { ++ /* ++ * Expect all flags except MTD_WRITEABLE to be ++ * equal on all subdevices. + */ +- if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) +- { ++ if ((concat->mtd.flags ^ subdev[i]-> ++ flags) & ~MTD_WRITEABLE) { + kfree(concat); +- printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device flags on \"%s\"\n", ++ subdev[i]->name); + return NULL; +- } +- else /* if writeable attribute differs, make super device writeable */ +- concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; ++ } else ++ /* if writeable attribute differs, ++ make super device writeable */ ++ concat->mtd.flags |= ++ subdev[i]->flags & MTD_WRITEABLE; + } + concat->mtd.size += subdev[i]->size; +- if(concat->mtd.oobblock != subdev[i]->oobblock || ++ if (concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || +- concat->mtd.eccsize != subdev[i]->eccsize) +- { ++ concat->mtd.eccsize != subdev[i]->eccsize || ++ !concat->mtd.read_ecc != !subdev[i]->read_ecc || ++ !concat->mtd.write_ecc != !subdev[i]->write_ecc || ++ !concat->mtd.read_oob != !subdev[i]->read_oob || ++ !concat->mtd.write_oob != !subdev[i]->write_oob) { + kfree(concat); +- printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible OOB or ECC data on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; +@@ -535,7 +752,6 @@ + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; + +- + /* + * Combine the erase block size info of the subdevices: + * +@@ -544,44 +760,44 @@ + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* if it differs from the last subdevice's erase size, count it */ ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; +- if(curr_erasesize > max_erasesize) ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j].erasesize != ++ curr_erasesize) { + ++num_erase_region; +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; +- if(curr_erasesize > max_erasesize) ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + +- if(num_erase_region == 1) +- { /* ++ if (num_erase_region == 1) { ++ /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; +- } +- else +- { /* ++ } else { ++ /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ +@@ -590,12 +806,13 @@ + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; +- concat->mtd.eraseregions = erase_region_p = kmalloc ( +- num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); +- if(!erase_region_p) +- { ++ concat->mtd.eraseregions = erase_region_p = ++ kmalloc(num_erase_region * ++ sizeof (struct mtd_erase_region_info), GFP_KERNEL); ++ if (!erase_region_p) { + kfree(concat); +- printk ("memory allocation error while creating erase region list" ++ printk ++ ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } +@@ -606,41 +823,48 @@ + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j]. ++ erasesize != curr_erasesize) { + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - ++ begin) / curr_erasesize; + begin = position; + +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; + ++erase_region_p; + } +- position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; ++ position += ++ subdev[i]->eraseregions[j]. ++ numblocks * curr_erasesize; + } + } + } +@@ -660,16 +884,14 @@ + void mtd_concat_destroy(struct mtd_info *mtd) + { + struct mtd_concat *concat = CONCAT(mtd); +- if(concat->mtd.numeraseregions) ++ if (concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); + } + +- + EXPORT_SYMBOL(mtd_concat_create); + EXPORT_SYMBOL(mtd_concat_destroy); + +- + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Robert Kaiser "); + MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); +--- linux-2.4.21/drivers/mtd/mtdcore.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdcore.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Core registration and callback routines for MTD + * drivers and users. +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #ifdef CONFIG_PROC_FS + #include +@@ -24,9 +25,15 @@ + + #include + +-static DECLARE_MUTEX(mtd_table_mutex); +-static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +-static struct mtd_notifier *mtd_notifiers = NULL; ++/* These are exported solely for the purpose of mtd_blkdevs.c. You ++ should not use them for _anything_ else */ ++DECLARE_MUTEX(mtd_table_mutex); ++struct mtd_info *mtd_table[MAX_MTD_DEVICES]; ++ ++EXPORT_SYMBOL_GPL(mtd_table_mutex); ++EXPORT_SYMBOL_GPL(mtd_table); ++ ++static LIST_HEAD(mtd_notifiers); + + /** + * add_mtd_device - register an MTD device +@@ -44,21 +51,28 @@ + + down(&mtd_table_mutex); + +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (!mtd_table[i]) +- { +- struct mtd_notifier *not=mtd_notifiers; ++ for (i=0; i < MAX_MTD_DEVICES; i++) ++ if (!mtd_table[i]) { ++ struct list_head *this; + + mtd_table[i] = mtd; + mtd->index = i; ++ mtd->usecount = 0; ++ + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); +- while (not) +- { +- (*(not->add))(mtd); +- not = not->next; ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->add(mtd); + } ++ + up(&mtd_table_mutex); +- MOD_INC_USE_COUNT; ++ /* We _know_ we aren't being removed, because ++ our caller is still holding us here. So none ++ of this try_ nonsense, and no bitching about it ++ either. :) */ ++ __module_get(THIS_MODULE); + return 0; + } + +@@ -78,29 +92,34 @@ + + int del_mtd_device (struct mtd_info *mtd) + { +- struct mtd_notifier *not=mtd_notifiers; +- int i; ++ int ret; + + down(&mtd_table_mutex); + +- for (i=0; i < MAX_MTD_DEVICES; i++) +- { +- if (mtd_table[i] == mtd) +- { +- while (not) +- { +- (*(not->remove))(mtd); +- not = not->next; +- } +- mtd_table[i] = NULL; +- up (&mtd_table_mutex); +- MOD_DEC_USE_COUNT; +- return 0; ++ if (mtd_table[mtd->index] != mtd) { ++ ret = -ENODEV; ++ } else if (mtd->usecount) { ++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", ++ mtd->index, mtd->name, mtd->usecount); ++ ret = -EBUSY; ++ } else { ++ struct list_head *this; ++ ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->remove(mtd); + } ++ ++ mtd_table[mtd->index] = NULL; ++ ++ module_put(THIS_MODULE); ++ ret = 0; + } + + up(&mtd_table_mutex); +- return 1; ++ return ret; + } + + /** +@@ -118,10 +137,9 @@ + + down(&mtd_table_mutex); + +- new->next = mtd_notifiers; +- mtd_notifiers = new; ++ list_add(&new->list, &mtd_notifiers); + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) +@@ -131,8 +149,8 @@ + } + + /** +- * register_mtd_user - unregister a 'user' of MTD devices. +- * @new: pointer to notifier info structure ++ * unregister_mtd_user - unregister a 'user' of MTD devices. ++ * @old: pointer to notifier info structure + * + * Removes a callback function pair from the list of 'users' to be + * notified upon addition or removal of MTD devices. Causes the +@@ -142,34 +160,24 @@ + + int unregister_mtd_user (struct mtd_notifier *old) + { +- struct mtd_notifier **prev = &mtd_notifiers; +- struct mtd_notifier *cur; + int i; + + down(&mtd_table_mutex); + +- while ((cur = *prev)) { +- if (cur == old) { +- *prev = cur->next; +- +- MOD_DEC_USE_COUNT; ++ module_put(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + old->remove(mtd_table[i]); + ++ list_del(&old->list); + up(&mtd_table_mutex); + return 0; +- } +- prev = &cur->next; +- } +- up(&mtd_table_mutex); +- return 1; + } + + + /** +- * __get_mtd_device - obtain a validated handle for an MTD device ++ * get_mtd_device - obtain a validated handle for an MTD device + * @mtd: last known address of the required MTD device + * @num: internal device number of the required MTD device + * +@@ -177,11 +185,10 @@ + * table, if any. Given an address and num == -1, search the device table + * for a device with that address and return if it's still present. Given + * both, return the num'th driver only if its address matches. Return NULL +- * if not. get_mtd_device() increases the use count, but +- * __get_mtd_device() doesn't - you should generally use get_mtd_device(). ++ * if not. + */ + +-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) ++struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) + { + struct mtd_info *ret = NULL; + int i; +@@ -198,16 +205,33 @@ + ret = NULL; + } + ++ if (ret && !try_module_get(ret->owner)) ++ ret = NULL; ++ ++ if (ret) ++ ret->usecount++; ++ + up(&mtd_table_mutex); + return ret; + } + ++void put_mtd_device(struct mtd_info *mtd) ++{ ++ int c; ++ ++ down(&mtd_table_mutex); ++ c = --mtd->usecount; ++ up(&mtd_table_mutex); ++ BUG_ON(c < 0); ++ ++ module_put(mtd->owner); ++} + + /* default_mtd_writev - default mtd writev method for MTD devices that + * dont implement their own + */ + +-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, ++int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) + { + unsigned long i; +@@ -237,7 +261,7 @@ + * implement their own + */ + +-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs, ++int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, + unsigned long count, loff_t from, size_t *retlen) + { + unsigned long i; +@@ -265,7 +289,8 @@ + + EXPORT_SYMBOL(add_mtd_device); + EXPORT_SYMBOL(del_mtd_device); +-EXPORT_SYMBOL(__get_mtd_device); ++EXPORT_SYMBOL(get_mtd_device); ++EXPORT_SYMBOL(put_mtd_device); + EXPORT_SYMBOL(register_mtd_user); + EXPORT_SYMBOL(unregister_mtd_user); + EXPORT_SYMBOL(default_mtd_writev); +@@ -308,10 +333,7 @@ + /* Support for /proc/mtd */ + + #ifdef CONFIG_PROC_FS +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + static struct proc_dir_entry *proc_mtd; +-#endif + + static inline int mtd_proc_info (char *buf, int i) + { +@@ -324,13 +346,8 @@ + this->erasesize, this->name); + } + +-static int mtd_read_proc ( char *page, char **start, off_t off,int count +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- ,int *eof, void *data_unused +-#else +- ,int unused +-#endif +- ) ++static int mtd_read_proc (char *page, char **start, off_t off, int count, ++ int *eof, void *data_unused) + { + int len, l, i; + off_t begin = 0; +@@ -350,9 +367,7 @@ + } + } + +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + *eof = 1; +-#endif + + done: + up(&mtd_table_mutex); +@@ -362,36 +377,16 @@ + return ((count < begin+len-off) ? count : begin+len-off); + } + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-struct proc_dir_entry mtd_proc_entry = { +- 0, /* low_ino: the inode -- dynamic */ +- 3, "mtd", /* len of name and name */ +- S_IFREG | S_IRUGO, /* mode */ +- 1, 0, 0, /* nlinks, owner, group */ +- 0, NULL, /* size - unused; operations -- use default */ +- &mtd_read_proc, /* function used to read data */ +- /* nothing more */ +- }; +-#endif +- + #endif /* CONFIG_PROC_FS */ + + /*====================================================================*/ + /* Init code */ + +-int __init init_mtd(void) ++static int __init init_mtd(void) + { + #ifdef CONFIG_PROC_FS +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if ((proc_mtd = create_proc_entry( "mtd", 0, 0 ))) ++ if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) + proc_mtd->read_proc = mtd_read_proc; +-#else +- proc_register_dynamic(&proc_root,&mtd_proc_entry); +-#endif +-#endif +- +-#if LINUX_VERSION_CODE < 0x20212 +- init_mtd_devices(); + #endif + + #ifdef CONFIG_PM +@@ -410,12 +405,8 @@ + #endif + + #ifdef CONFIG_PROC_FS +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (proc_mtd) +- remove_proc_entry( "mtd", 0); +-#else +- proc_unregister(&proc_root,mtd_proc_entry.low_ino); +-#endif ++ remove_proc_entry( "mtd", NULL); + #endif + } + +--- linux-2.4.21/drivers/mtd/mtdpart.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/mtdpart.c +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id$ ++ * $Id$ + * + * 02-21-2002 Thomas Gleixner + * added support for read_oob, write_oob +@@ -16,10 +16,11 @@ + #include + #include + #include +- ++#include ++#include + #include + #include +- ++#include + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -54,8 +55,12 @@ + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; ++ if (part->master->read_ecc == NULL) + return part->master->read (part->master, from + part->offset, + len, retlen, buf); ++ else ++ return part->master->read_ecc (part->master, from + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); + } + + static int part_point (struct mtd_info *mtd, loff_t from, size_t len, +@@ -78,9 +83,11 @@ + + + static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) ++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) +@@ -109,14 +116,28 @@ + len, retlen, buf); + } + ++static int part_get_user_prot_info (struct mtd_info *mtd, ++ struct otp_info *buf, size_t len) ++{ ++ struct mtd_part *part = PART(mtd); ++ return part->master->get_user_prot_info (part->master, buf, len); ++} ++ + static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); +- return part->master->read_user_prot_reg (part->master, from, ++ return part->master->read_fact_prot_reg (part->master, from, + len, retlen, buf); + } + ++static int part_get_fact_prot_info (struct mtd_info *mtd, ++ struct otp_info *buf, size_t len) ++{ ++ struct mtd_part *part = PART(mtd); ++ return part->master->get_fact_prot_info (part->master, buf, len); ++} ++ + static int part_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) + { +@@ -127,17 +148,24 @@ + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; ++ if (part->master->write_ecc == NULL) + return part->master->write (part->master, to + part->offset, + len, retlen, buf); ++ else ++ return part->master->write_ecc (part->master, to + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); ++ + } + + static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) +@@ -168,41 +196,61 @@ + len, retlen, buf); + } + +-static int part_writev (struct mtd_info *mtd, const struct iovec *vecs, ++static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) ++{ ++ struct mtd_part *part = PART(mtd); ++ return part->master->lock_user_prot_reg (part->master, from, len); ++} ++ ++static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (part->master->writev_ecc == NULL) + return part->master->writev (part->master, vecs, count, + to + part->offset, retlen); ++ else ++ return part->master->writev_ecc (part->master, vecs, count, ++ to + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + +-static int part_readv (struct mtd_info *mtd, struct iovec *vecs, ++static int part_readv (struct mtd_info *mtd, struct kvec *vecs, + unsigned long count, loff_t from, size_t *retlen) + { + struct mtd_part *part = PART(mtd); ++ if (part->master->readv_ecc == NULL) + return part->master->readv (part->master, vecs, count, + from + part->offset, retlen); ++ else ++ return part->master->readv_ecc (part->master, vecs, count, ++ from + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + +-static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, ++static int part_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->writev_ecc (part->master, vecs, count, + to + part->offset, retlen, + eccbuf, oobsel); + } + +-static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs, ++static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, + unsigned long count, loff_t from, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->readv_ecc (part->master, vecs, count, + from + part->offset, retlen, + eccbuf, oobsel); +@@ -211,13 +259,29 @@ + static int part_erase (struct mtd_info *mtd, struct erase_info *instr) + { + struct mtd_part *part = PART(mtd); ++ int ret; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (instr->addr >= mtd->size) + return -EINVAL; + instr->addr += part->offset; +- return part->master->erase(part->master, instr); ++ ret = part->master->erase(part->master, instr); ++ return ret; ++} ++ ++void mtd_erase_callback(struct erase_info *instr) ++{ ++ if (instr->mtd->erase == part_erase) { ++ struct mtd_part *part = PART(instr->mtd); ++ ++ if (instr->fail_addr != 0xffffffff) ++ instr->fail_addr -= part->offset; ++ instr->addr -= part->offset; ++ } ++ if (instr->callback) ++ instr->callback(instr); + } ++EXPORT_SYMBOL_GPL(mtd_erase_callback); + + static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) + { +@@ -253,6 +317,26 @@ + part->master->resume(part->master); + } + ++static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_part *part = PART(mtd); ++ if (ofs >= mtd->size) ++ return -EINVAL; ++ ofs += part->offset; ++ return part->master->block_isbad(part->master, ofs); ++} ++ ++static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_part *part = PART(mtd); ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ if (ofs >= mtd->size) ++ return -EINVAL; ++ ofs += part->offset; ++ return part->master->block_markbad(part->master, ofs); ++} ++ + /* + * This function unregisters and destroy all slave MTD objects which are + * attached to the given master MTD object. +@@ -288,7 +372,7 @@ + */ + + int add_mtd_partitions(struct mtd_info *master, +- struct mtd_partition *parts, ++ const struct mtd_partition *parts, + int nbparts) + { + struct mtd_part *slave; +@@ -321,7 +405,7 @@ + + slave->mtd.name = parts[i].name; + slave->mtd.bank_size = master->bank_size; +- slave->mtd.module = master->module; ++ slave->mtd.owner = master->owner; + + slave->mtd.read = part_read; + slave->mtd.write = part_write; +@@ -345,6 +429,12 @@ + slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; + if(master->write_user_prot_reg) + slave->mtd.write_user_prot_reg = part_write_user_prot_reg; ++ if(master->lock_user_prot_reg) ++ slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; ++ if(master->get_user_prot_info) ++ slave->mtd.get_user_prot_info = part_get_user_prot_info; ++ if(master->get_fact_prot_info) ++ slave->mtd.get_fact_prot_info = part_get_fact_prot_info; + if (master->sync) + slave->mtd.sync = part_sync; + if (!i && master->suspend && master->resume) { +@@ -363,6 +453,10 @@ + slave->mtd.lock = part_lock; + if (master->unlock) + slave->mtd.unlock = part_unlock; ++ if (master->block_isbad) ++ slave->mtd.block_isbad = part_block_isbad; ++ if (master->block_markbad) ++ slave->mtd.block_markbad = part_block_markbad; + slave->mtd.erase = part_erase; + slave->master = master; + slave->offset = parts[i].offset; +@@ -433,6 +527,9 @@ + parts[i].name); + } + ++ /* copy oobinfo from master */ ++ memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo)); ++ + if(parts[i].mtdp) + { /* store the object pointer (caller may or may not register it */ + *parts[i].mtdp = &slave->mtd; +@@ -452,6 +549,75 @@ + EXPORT_SYMBOL(add_mtd_partitions); + EXPORT_SYMBOL(del_mtd_partitions); + ++static DEFINE_SPINLOCK(part_parser_lock); ++static LIST_HEAD(part_parsers); ++ ++static struct mtd_part_parser *get_partition_parser(const char *name) ++{ ++ struct list_head *this; ++ void *ret = NULL; ++ spin_lock(&part_parser_lock); ++ ++ list_for_each(this, &part_parsers) { ++ struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); ++ ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++int register_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_add(&p->list, &part_parsers); ++ spin_unlock(&part_parser_lock); ++ ++ return 0; ++} ++ ++int deregister_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_del(&p->list); ++ spin_unlock(&part_parser_lock); ++ return 0; ++} ++ ++int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin) ++{ ++ struct mtd_part_parser *parser; ++ int ret = 0; ++ ++ for ( ; ret <= 0 && *types; types++) { ++ parser = get_partition_parser(*types); ++#ifdef CONFIG_KMOD ++ if (!parser && !request_module("%s", *types)) ++ parser = get_partition_parser(*types); ++#endif ++ if (!parser) { ++ printk(KERN_NOTICE "%s partition parsing not available\n", ++ *types); ++ continue; ++ } ++ ret = (*parser->parse_fn)(master, pparts, origin); ++ if (ret > 0) { ++ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ } ++ put_partition_parser(parser); ++ } ++ return ret; ++} ++ ++EXPORT_SYMBOL_GPL(parse_mtd_partitions); ++EXPORT_SYMBOL_GPL(register_mtd_parser); ++EXPORT_SYMBOL_GPL(deregister_mtd_parser); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Nicolas Pitre "); +--- linux-2.4.21/drivers/mtd/nand/Config.in~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/Config.in +@@ -1,6 +1,6 @@ + # drivers/mtd/nand/Config.in + +-# $Id$ ++# $Id$ + + mainmenu_option next_comment + +@@ -11,26 +11,56 @@ + bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE + fi + +-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then +- dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND ++if [ "$CONFIG_ARM" = "y" ]; then ++ dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T ++ dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP ++ dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12 ++ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312 + fi + +-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then +- dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND ++if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then ++ define_bool CONFIG_MTD_NAND_IDS y ++else ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then ++ define_bool CONFIG_MTD_NAND_IDS m ++ fi + fi + +-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then +- dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND ++if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then ++ dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND + fi + +-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then +- define_bool CONFIG_MTD_NAND_IDS y ++if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then ++ dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND + fi + +-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then +-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then +- define_bool CONFIG_MTD_NAND_IDS m ++if [ "$CONFIG_PPCHAMELEONEVB" = "y" ]; then ++ dep_tristate ' NAND Flash device on PPChameleonEVB board' CONFIG_MTD_NAND_PPCHAMELEONEVB $CONFIG_MTD_NAND ++fi ++ ++if [ "$CONFIG_SOC_AU1550" = "y" ]; then ++ dep_tristate ' NAND Flash Driver for Au1550 controller' CONFIG_MTD_NAND_AU1550 $CONFIG_MTD_NAND + fi ++ ++if [ "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then ++ dep_tristate ' Renesas Flash ROM 4-slot interface board (FROM_BOARD4)' CONFIG_MTD_NAND_RTC_FROM4 $CONFIG_MTD_NAND ++ if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "y" ]; then ++ define_bool CONFIG_REED_SOLOMON y ++ define_bool CONFIG_REED_SOLOMON_DEC8 y ++ else ++ if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "m" ]; then ++ define_bool CONFIG_REED_SOLOMON m ++ define_bool CONFIG_REED_SOLOMON_DEC8 m ++ fi ++ fi ++fi ++ ++dep_tristate ' DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)' CONFIG_MTD_NAND_DISKONCHIP $CONFIG_MTD_NAND $CONFIG_EXPERIMENTAL ++if [ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then ++ bool ' Advanced detection options for DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED ++ hex ' Physical address of DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS ++ bool ' Probe high addresses' CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH $CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED ++ bool ' Allow BBT write on DiskOnChip Millennium and 2000TSOP' CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE + fi + + endmenu +--- linux-2.4.21/drivers/mtd/nand/Makefile~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/Makefile +@@ -1,16 +1,16 @@ + # +-# linux/drivers/nand/Makefile ++# linux/drivers/nand/Makefile.24 ++# Makefile for obsolete kernels. + # +-# $Id$ ++# $Id$ + + O_TARGET := nandlink.o ++export-objs := nand_base.o nand_bbt.o nand_ecc.o nand_ids.o ++list-multi := nand.o + +-export-objs := nand.o nand_ecc.o nand_ids.o +- +-obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +-obj-$(CONFIG_MTD_NAND_SPIA) += spia.o +-obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +-obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o +-obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o ++include Makefile.common + + include $(TOPDIR)/Rules.make ++ ++nand.o: $(nand-objs) ++ $(LD) -r -o $@ $(nand-objs) +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/Makefile.common +@@ -0,0 +1,24 @@ ++# ++# linux/drivers/nand/Makefile ++# ++# $Id$ ++ ++obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o ++obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o ++ ++obj-$(CONFIG_MTD_NAND_SPIA) += spia.o ++obj-$(CONFIG_MTD_NAND_TOTO) += toto.o ++obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o ++obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o ++obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o ++obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o ++obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o ++obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o ++obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o ++obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o ++obj-$(CONFIG_MTD_NAND_H1900) += h1910.o ++obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o ++obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o ++obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o ++ ++nand-objs = nand_base.o nand_bbt.o +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/au1550nd.c +@@ -0,0 +1,477 @@ ++/* ++ * drivers/mtd/nand/au1550nd.c ++ * ++ * Copyright (C) 2004 Embedded Edge, LLC ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* fixme: this is ugly */ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) ++#include ++#ifdef CONFIG_MIPS_PB1550 ++#include ++#endif ++#ifdef CONFIG_MIPS_DB1550 ++#include ++#endif ++#else ++#include ++#ifdef CONFIG_MIPS_PB1550 ++#include ++#endif ++#ifdef CONFIG_MIPS_DB1550 ++#include ++#endif ++#endif ++ ++/* ++ * MTD structure for NAND controller ++ */ ++static struct mtd_info *au1550_mtd = NULL; ++static void __iomem *p_nand; ++static int nand_width = 1; /* default x8*/ ++ ++#define NAND_CS 1 ++ ++/* ++ * Define partitions for flash device ++ */ ++const static struct mtd_partition partition_info[] = { ++#ifdef CONFIG_MIPS_PB1550 ++#define NUM_PARTITIONS 2 ++ { ++ .name = "Pb1550 NAND FS 0", ++ .offset = 0, ++ .size = 8*1024*1024 ++ }, ++ { ++ .name = "Pb1550 NAND FS 1", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL ++ } ++#endif ++#ifdef CONFIG_MIPS_DB1550 ++#define NUM_PARTITIONS 2 ++ { ++ .name = "Db1550 NAND FS 0", ++ .offset = 0, ++ .size = 8*1024*1024 ++ }, ++ { ++ .name = "Db1550 NAND FS 1", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL ++ } ++#endif ++}; ++ ++ ++/** ++ * au_read_byte - read one byte from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 8bit buswith ++ */ ++static u_char au_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ u_char ret = readb(this->IO_ADDR_R); ++ au_sync(); ++ return ret; ++} ++ ++/** ++ * au_write_byte - write one byte to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * write function for 8it buswith ++ */ ++static void au_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writeb(byte, this->IO_ADDR_W); ++ au_sync(); ++} ++ ++/** ++ * au_read_byte16 - read one byte endianess aware from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 16bit buswith with ++ * endianess conversion ++ */ ++static u_char au_read_byte16(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); ++ au_sync(); ++ return ret; ++} ++ ++/** ++ * au_write_byte16 - write one byte endianess aware to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * write function for 16bit buswith with ++ * endianess conversion ++ */ ++static void au_write_byte16(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); ++ au_sync(); ++} ++ ++/** ++ * au_read_word - read one word from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 16bit buswith without ++ * endianess conversion ++ */ ++static u16 au_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ u16 ret = readw(this->IO_ADDR_R); ++ au_sync(); ++ return ret; ++} ++ ++/** ++ * au_write_word - write one word to the chip ++ * @mtd: MTD device structure ++ * @word: data word to write ++ * ++ * write function for 16bit buswith without ++ * endianess conversion ++ */ ++static void au_write_word(struct mtd_info *mtd, u16 word) ++{ ++ struct nand_chip *this = mtd->priv; ++ writew(word, this->IO_ADDR_W); ++ au_sync(); ++} ++ ++/** ++ * au_write_buf - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * write function for 8bit buswith ++ */ ++static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++ au_sync(); ++ } ++} ++ ++/** ++ * au_read_buf - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * read function for 8bit buswith ++ */ ++static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++ au_sync(); ++ } ++} ++ ++/** ++ * au_verify_buf - Verify chip data against buffer ++ * @mtd: MTD device structure ++ * @buf: buffer containing the data to compare ++ * @len: number of bytes to compare ++ * ++ * verify function for 8bit buswith ++ */ ++static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ au_sync(); ++ } ++ ++ return 0; ++} ++ ++/** ++ * au_write_buf16 - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * write function for 16bit buswith ++ */ ++static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_W); ++ au_sync(); ++ } ++ ++} ++ ++/** ++ * au_read_buf16 - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * read function for 16bit buswith ++ */ ++static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_R); ++ au_sync(); ++ } ++} ++ ++/** ++ * au_verify_buf16 - Verify chip data against buffer ++ * @mtd: MTD device structure ++ * @buf: buffer containing the data to compare ++ * @len: number of bytes to compare ++ * ++ * verify function for 16bit buswith ++ */ ++static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ au_sync(); ++ } ++ return 0; ++} ++ ++ ++static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break; ++ case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break; ++ ++ case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break; ++ case NAND_CTL_CLRALE: ++ this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; ++ /* FIXME: Nobody knows why this is neccecary, ++ * but it works only that way */ ++ udelay(1); ++ break; ++ ++ case NAND_CTL_SETNCE: ++ /* assert (force assert) chip enable */ ++ au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break; ++ break; ++ ++ case NAND_CTL_CLRNCE: ++ /* deassert chip enable */ ++ au_writel(0, MEM_STNDCTL); break; ++ break; ++ } ++ ++ this->IO_ADDR_R = this->IO_ADDR_W; ++ ++ /* Drain the writebuffer */ ++ au_sync(); ++} ++ ++int au1550_device_ready(struct mtd_info *mtd) ++{ ++ int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; ++ au_sync(); ++ return ret; ++} ++ ++/* ++ * Main initialization routine ++ */ ++int __init au1550_init (void) ++{ ++ struct nand_chip *this; ++ u16 boot_swapboot = 0; /* default value */ ++ int retval; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ au1550_mtd = kmalloc (sizeof(struct mtd_info) + ++ sizeof (struct nand_chip), GFP_KERNEL); ++ if (!au1550_mtd) { ++ printk ("Unable to allocate NAND MTD dev structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&au1550_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) au1550_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ au1550_mtd->priv = this; ++ ++ ++ /* MEM_STNDCTL: disable ints, disable nand boot */ ++ au_writel(0, MEM_STNDCTL); ++ ++#ifdef CONFIG_MIPS_PB1550 ++ /* set gpio206 high */ ++ au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR); ++ ++ boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | ++ ((bcsr->status >> 6) & 0x1); ++ switch (boot_swapboot) { ++ case 0: ++ case 2: ++ case 8: ++ case 0xC: ++ case 0xD: ++ /* x16 NAND Flash */ ++ nand_width = 0; ++ break; ++ case 1: ++ case 9: ++ case 3: ++ case 0xE: ++ case 0xF: ++ /* x8 NAND Flash */ ++ nand_width = 1; ++ break; ++ default: ++ printk("Pb1550 NAND: bad boot:swap\n"); ++ retval = -EINVAL; ++ goto outmem; ++ } ++#endif ++ ++ /* Configure RCE1 - should be done by YAMON */ ++ au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */ ++ au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */ ++ au_sync(); ++ ++ /* setup and enable chip select, MEM_STADDR1 */ ++ /* we really need to decode offsets only up till 0x20 */ ++ au_writel((1<<28) | (NAND_PHYS_ADDR>>4) | ++ (((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18), ++ MEM_STADDR1); ++ au_sync(); ++ ++ p_nand = ioremap(NAND_PHYS_ADDR, 0x1000); ++ ++ /* Set address of hardware control function */ ++ this->hwcontrol = au1550_hwcontrol; ++ this->dev_ready = au1550_device_ready; ++ /* 30 us command delay time */ ++ this->chip_delay = 30; ++ this->eccmode = NAND_ECC_SOFT; ++ ++ this->options = NAND_NO_AUTOINCR; ++ ++ if (!nand_width) ++ this->options |= NAND_BUSWIDTH_16; ++ ++ this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; ++ this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; ++ this->write_word = au_write_word; ++ this->read_word = au_read_word; ++ this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; ++ this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; ++ this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (au1550_mtd, 1)) { ++ retval = -ENXIO; ++ goto outio; ++ } ++ ++ /* Register the partitions */ ++ add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS); ++ ++ return 0; ++ ++ outio: ++ iounmap ((void *)p_nand); ++ ++ outmem: ++ kfree (au1550_mtd); ++ return retval; ++} ++ ++module_init(au1550_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit au1550_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1]; ++ ++ /* Release resources, unregister device */ ++ nand_release (au1550_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (au1550_mtd); ++ ++ /* Unmap */ ++ iounmap ((void *)p_nand); ++} ++module_exit(au1550_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Embedded Edge, LLC"); ++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); +--- linux-2.4.21/drivers/mtd/nand/autcpu12.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/autcpu12.c +@@ -4,9 +4,9 @@ + * Copyright (c) 2002 Thomas Gleixner + * + * Derived from drivers/mtd/spia.c +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * +- * $Id$ ++ * $Id$ + * + * 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 +@@ -15,7 +15,7 @@ + * Overview: + * This is a device driver for the NAND flash device found on the + * autronix autcpu12 board, which is a SmartMediaCard. It supports +- * 16MB, 32MB and 64MB cards. ++ * 16MiB, 32MiB and 64MiB cards. + * + * + * 02-12-2002 TG Cleanup of module params +@@ -25,10 +25,11 @@ + * added page_cache + * + * 10-06-2002 TG 128K card support added +- * + */ + ++#include + #include ++#include + #include + #include + #include +@@ -43,68 +44,49 @@ + */ + static struct mtd_info *autcpu12_mtd = NULL; + +-/* +- * Module stuff +- */ +-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +-#define autcpu12_init init_module +-#define autcpu12_cleanup cleanup_module +-#endif +- + static int autcpu12_io_base = CS89712_VIRT_BASE; + static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC; + static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET; + static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET; +-static int autcpu12_fio_base; +- +-#ifdef MODULE +-MODULE_PARM(autcpu12_fio_pbase, "i"); +-MODULE_PARM(autcpu12_fio_ctrl, "i"); +-MODULE_PARM(autcpu12_pedr, "i"); +- +-__setup("autcpu12_fio_pbase=",autcpu12_fio_pbase); +-__setup("autcpu12_fio_ctrl=",autcpu12_fio_ctrl); +-__setup("autcpu12_pedr=",autcpu12_pedr); +-#endif ++static void __iomem * autcpu12_fio_base; + + /* + * Define partitions for flash devices + */ +- + static struct mtd_partition partition_info16k[] = { +- { name: "AUTCPU12 flash partition 1", +- offset: 0, +- size: 8 * SZ_1M }, +- { name: "AUTCPU12 flash partition 2", +- offset: 8 * SZ_1M, +- size: 8 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 1", ++ .offset = 0, ++ .size = 8 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 2", ++ .offset = 8 * SZ_1M, ++ .size = 8 * SZ_1M }, + }; + + static struct mtd_partition partition_info32k[] = { +- { name: "AUTCPU12 flash partition 1", +- offset: 0, +- size: 8 * SZ_1M }, +- { name: "AUTCPU12 flash partition 2", +- offset: 8 * SZ_1M, +- size: 24 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 1", ++ .offset = 0, ++ .size = 8 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 2", ++ .offset = 8 * SZ_1M, ++ .size = 24 * SZ_1M }, + }; + + static struct mtd_partition partition_info64k[] = { +- { name: "AUTCPU12 flash partition 1", +- offset: 0, +- size: 16 * SZ_1M }, +- { name: "AUTCPU12 flash partition 2", +- offset: 16 * SZ_1M, +- size: 48 * SZ_1M}, ++ { .name = "AUTCPU12 flash partition 1", ++ .offset = 0, ++ .size = 16 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 2", ++ .offset = 16 * SZ_1M, ++ .size = 48 * SZ_1M }, + }; + + static struct mtd_partition partition_info128k[] = { +- { name: "AUTCPU12 flash partition 1", +- offset: 0, +- size: 16 * SZ_1M }, +- { name: "AUTCPU12 flash partition 2", +- offset: 16 * SZ_1M, +- size: 112 * SZ_1M}, ++ { .name = "AUTCPU12 flash partition 1", ++ .offset = 0, ++ .size = 16 * SZ_1M }, ++ { .name = "AUTCPU12 flash partition 2", ++ .offset = 16 * SZ_1M, ++ .size = 112 * SZ_1M }, + }; + + #define NUM_PARTITIONS16K 2 +@@ -114,7 +96,7 @@ + /* + * hardware specific access to control-lines + */ +-void autcpu12_hwcontrol(int cmd) ++static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) + { + + switch(cmd){ +@@ -133,12 +115,13 @@ + /* + * read device ready pin + */ +-int autcpu12_device_ready(void) ++int autcpu12_device_ready(struct mtd_info *mtd) + { + + return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; + + } ++ + /* + * Main initialization routine + */ +@@ -157,7 +140,7 @@ + } + + /* map physical adress */ +- autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K); ++ autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K); + if(!autcpu12_fio_base){ + printk("Ioremap autcpu12 SmartMedia Card failed\n"); + err = -EIO; +@@ -183,29 +166,18 @@ + this->chip_delay = 20; + this->eccmode = NAND_ECC_SOFT; + ++ /* Enable the following for a flash based bad block table */ ++ /* ++ this->options = NAND_USE_FLASH_BBT; ++ */ ++ this->options = NAND_USE_FLASH_BBT; ++ + /* Scan to find existance of the device */ +- if (nand_scan (autcpu12_mtd)) { ++ if (nand_scan (autcpu12_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + +- /* Allocate memory for internal data buffer */ +- this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); +- if (!this->data_buf) { +- printk ("Unable to allocate NAND data buffer for AUTCPU12.\n"); +- err = -ENOMEM; +- goto out_ior; +- } +- +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for AUTCPU12.\n"); +- err = -ENOMEM; +- goto out_buf; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + switch(autcpu12_mtd->size){ + case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; +@@ -215,15 +187,11 @@ + default: { + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; +- goto out_cac; ++ goto out_ior; + } + } + goto out; + +-out_cac: +- kfree (this->data_cache); +-out_buf: +- kfree (this->data_buf); + out_ior: + iounmap((void *)autcpu12_fio_base); + out_mtd: +@@ -240,17 +208,8 @@ + #ifdef MODULE + static void __exit autcpu12_cleanup (void) + { +- struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1]; +- +- /* Unregister partitions */ +- del_mtd_partitions(autcpu12_mtd); +- +- /* Unregister the device */ +- del_mtd_device (autcpu12_mtd); +- +- /* Free internal data buffers */ +- kfree (this->data_buf); +- kfree (this->data_cache); ++ /* Release resources, unregister device */ ++ nand_release (autcpu12_mtd); + + /* unmap physical adress */ + iounmap((void *)autcpu12_fio_base); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/diskonchip.c +@@ -0,0 +1,1780 @@ ++/* ++ * drivers/mtd/nand/diskonchip.c ++ * ++ * (C) 2003 Red Hat, Inc. ++ * (C) 2004 Dan Brown ++ * (C) 2004 Kalev Lember ++ * ++ * Author: David Woodhouse ++ * Additional Diskonchip 2000 and Millennium support by Dan Brown ++ * Diskonchip Millennium Plus support by Kalev Lember ++ * ++ * Error correction code lifted from the old docecc code ++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) ++ * Copyright (C) 2000 Netgem S.A. ++ * converted to the generic Reed-Solomon library by Thomas Gleixner ++ * ++ * Interface to generic NAND code for M-Systems DiskOnChip devices ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Where to look for the devices? */ ++#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS ++#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 ++#endif ++ ++static unsigned long __initdata doc_locations[] = { ++#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) ++#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH ++ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, ++ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, ++ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, ++ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, ++ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, ++#else /* CONFIG_MTD_DOCPROBE_HIGH */ ++ 0xc8000, 0xca000, 0xcc000, 0xce000, ++ 0xd0000, 0xd2000, 0xd4000, 0xd6000, ++ 0xd8000, 0xda000, 0xdc000, 0xde000, ++ 0xe0000, 0xe2000, 0xe4000, 0xe6000, ++ 0xe8000, 0xea000, 0xec000, 0xee000, ++#endif /* CONFIG_MTD_DOCPROBE_HIGH */ ++#elif defined(__PPC__) ++ 0xe4000000, ++#elif defined(CONFIG_MOMENCO_OCELOT) ++ 0x2f000000, ++ 0xff000000, ++#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) ++ 0xff000000, ++##else ++#warning Unknown architecture for DiskOnChip. No default probe locations defined ++#endif ++ 0xffffffff }; ++ ++static struct mtd_info *doclist = NULL; ++ ++struct doc_priv { ++ void __iomem *virtadr; ++ unsigned long physadr; ++ u_char ChipID; ++ u_char CDSNControl; ++ int chips_per_floor; /* The number of chips detected on each floor */ ++ int curfloor; ++ int curchip; ++ int mh0_page; ++ int mh1_page; ++ struct mtd_info *nextdoc; ++}; ++ ++/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL ++ MediaHeader. The spec says to just keep going, I think, but that's just ++ silly. */ ++#define MAX_MEDIAHEADER_SCAN 8 ++ ++/* This is the syndrome computed by the HW ecc generator upon reading an empty ++ page, one with all 0xff for data and stored ecc code. */ ++static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; ++/* This is the ecc value computed by the HW ecc generator upon writing an empty ++ page, one with all 0xff for data. */ ++static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; ++ ++#define INFTL_BBT_RESERVED_BLOCKS 4 ++ ++#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) ++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) ++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); ++static void doc200x_select_chip(struct mtd_info *mtd, int chip); ++ ++static int debug=0; ++module_param(debug, int, 0); ++ ++static int try_dword=1; ++module_param(try_dword, int, 0); ++ ++static int no_ecc_failures=0; ++module_param(no_ecc_failures, int, 0); ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static int no_autopart=0; ++module_param(no_autopart, int, 0); ++#endif ++ ++#ifdef MTD_NAND_DISKONCHIP_BBTWRITE ++static int inftl_bbt_write=1; ++#else ++static int inftl_bbt_write=0; ++#endif ++module_param(inftl_bbt_write, int, 0); ++ ++static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; ++module_param(doc_config_location, ulong, 0); ++MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); ++ ++ ++/* Sector size for HW ECC */ ++#define SECTOR_SIZE 512 ++/* The sector bytes are packed into NB_DATA 10 bit words */ ++#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) ++/* Number of roots */ ++#define NROOTS 4 ++/* First consective root */ ++#define FCR 510 ++/* Number of symbols */ ++#define NN 1023 ++ ++/* the Reed Solomon control structure */ ++static struct rs_control *rs_decoder; ++ ++/* ++ * The HW decoder in the DoC ASIC's provides us a error syndrome, ++ * which we must convert to a standard syndrom usable by the generic ++ * Reed-Solomon library code. ++ * ++ * Fabrice Bellard figured this out in the old docecc code. I added ++ * some comments, improved a minor bit and converted it to make use ++ * of the generic Reed-Solomon libary. tglx ++ */ ++static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) ++{ ++ int i, j, nerr, errpos[8]; ++ uint8_t parity; ++ uint16_t ds[4], s[5], tmp, errval[8], syn[4]; ++ ++ /* Convert the ecc bytes into words */ ++ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); ++ ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); ++ ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); ++ ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); ++ parity = ecc[1]; ++ ++ /* Initialize the syndrom buffer */ ++ for (i = 0; i < NROOTS; i++) ++ s[i] = ds[0]; ++ /* ++ * Evaluate ++ * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] ++ * where x = alpha^(FCR + i) ++ */ ++ for(j = 1; j < NROOTS; j++) { ++ if(ds[j] == 0) ++ continue; ++ tmp = rs->index_of[ds[j]]; ++ for(i = 0; i < NROOTS; i++) ++ s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; ++ } ++ ++ /* Calc s[i] = s[i] / alpha^(v + i) */ ++ for (i = 0; i < NROOTS; i++) { ++ if (syn[i]) ++ syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); ++ } ++ /* Call the decoder library */ ++ nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); ++ ++ /* Incorrectable errors ? */ ++ if (nerr < 0) ++ return nerr; ++ ++ /* ++ * Correct the errors. The bitpositions are a bit of magic, ++ * but they are given by the design of the de/encoder circuit ++ * in the DoC ASIC's. ++ */ ++ for(i = 0;i < nerr; i++) { ++ int index, bitpos, pos = 1015 - errpos[i]; ++ uint8_t val; ++ if (pos >= NB_DATA && pos < 1019) ++ continue; ++ if (pos < NB_DATA) { ++ /* extract bit position (MSB first) */ ++ pos = 10 * (NB_DATA - 1 - pos) - 6; ++ /* now correct the following 10 bits. At most two bytes ++ can be modified since pos is even */ ++ index = (pos >> 3) ^ 1; ++ bitpos = pos & 7; ++ if ((index >= 0 && index < SECTOR_SIZE) || ++ index == (SECTOR_SIZE + 1)) { ++ val = (uint8_t) (errval[i] >> (2 + bitpos)); ++ parity ^= val; ++ if (index < SECTOR_SIZE) ++ data[index] ^= val; ++ } ++ index = ((pos >> 3) + 1) ^ 1; ++ bitpos = (bitpos + 10) & 7; ++ if (bitpos == 0) ++ bitpos = 8; ++ if ((index >= 0 && index < SECTOR_SIZE) || ++ index == (SECTOR_SIZE + 1)) { ++ val = (uint8_t)(errval[i] << (8 - bitpos)); ++ parity ^= val; ++ if (index < SECTOR_SIZE) ++ data[index] ^= val; ++ } ++ } ++ } ++ /* If the parity is wrong, no rescue possible */ ++ return parity ? -1 : nerr; ++} ++ ++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) ++{ ++ volatile char dummy; ++ int i; ++ ++ for (i = 0; i < cycles; i++) { ++ if (DoC_is_Millennium(doc)) ++ dummy = ReadDOC(doc->virtadr, NOP); ++ else if (DoC_is_MillenniumPlus(doc)) ++ dummy = ReadDOC(doc->virtadr, Mplus_NOP); ++ else ++ dummy = ReadDOC(doc->virtadr, DOCStatus); ++ } ++ ++} ++ ++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) ++ ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(struct doc_priv *doc) ++{ ++ void __iomem *docptr = doc->virtadr; ++ unsigned long timeo = jiffies + (HZ * 10); ++ ++ if(debug) printk("_DoC_WaitReady...\n"); ++ /* Out-of-line routine to wait for chip response */ ++ if (DoC_is_MillenniumPlus(doc)) { ++ while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ } else { ++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ } ++ ++ return 0; ++} ++ ++static inline int DoC_WaitReady(struct doc_priv *doc) ++{ ++ void __iomem *docptr = doc->virtadr; ++ int ret = 0; ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ DoC_Delay(doc, 4); ++ ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ } else { ++ DoC_Delay(doc, 4); ++ ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ DoC_Delay(doc, 2); ++ } ++ ++ if(debug) printk("DoC_WaitReady OK\n"); ++ return ret; ++} ++ ++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ if(debug)printk("write_byte %02x\n", datum); ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, 2k_CDSN_IO); ++} ++ ++static u_char doc2000_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ u_char ret; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ DoC_Delay(doc, 2); ++ ret = ReadDOC(docptr, 2k_CDSN_IO); ++ if (debug) printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++ ++static void doc2000_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ if (debug)printk("writebuf of %d bytes: ", len); ++ for (i=0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) printk("\n"); ++} ++ ++static void doc2000_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("readbuf of %d bytes: ", len); ++ ++ for (i=0; i < len; i++) { ++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++} ++ ++static void doc2000_readbuf_dword(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug) printk("readbuf_dword of %d bytes: ", len); ++ ++ if (unlikely((((unsigned long)buf)|len) & 3)) { ++ for (i=0; i < len; i++) { ++ *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++ } else { ++ for (i=0; i < len; i+=4) { ++ *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); ++ } ++ } ++} ++ ++static int doc2000_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) ++ return -EFAULT; ++ return 0; ++} ++ ++static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ uint16_t ret; ++ ++ doc200x_select_chip(mtd, nr); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ this->write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ /* We cant' use dev_ready here, but at least we wait for the ++ * command to complete ++ */ ++ udelay(50); ++ ++ ret = this->read_byte(mtd) << 8; ++ ret |= this->read_byte(mtd); ++ ++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { ++ /* First chip probe. See if we get same results by 32-bit access */ ++ union { ++ uint32_t dword; ++ uint8_t byte[4]; ++ } ident; ++ void __iomem *docptr = doc->virtadr; ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ doc2000_write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ doc2000_write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ udelay(50); ++ ++ ident.dword = readl(docptr + DoC_2k_CDSN_IO); ++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { ++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); ++ this->read_buf = &doc2000_readbuf_dword; ++ } ++ } ++ ++ return ret; ++} ++ ++static void __init doc2000_count_chips(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ uint16_t mfrid; ++ int i; ++ ++ /* Max 4 chips per floor on DiskOnChip 2000 */ ++ doc->chips_per_floor = 4; ++ ++ /* Find out what the first chip is */ ++ mfrid = doc200x_ident_chip(mtd, 0); ++ ++ /* Find how many chips in each floor. */ ++ for (i = 1; i < 4; i++) { ++ if (doc200x_ident_chip(mtd, i) != mfrid) ++ break; ++ } ++ doc->chips_per_floor = i; ++ printk(KERN_DEBUG "Detected %d chips per floor.\n", i); ++} ++ ++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) ++{ ++ struct doc_priv *doc = this->priv; ++ ++ int status; ++ ++ DoC_WaitReady(doc); ++ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); ++ DoC_WaitReady(doc); ++ status = (int)this->read_byte(mtd); ++ ++ return status; ++} ++ ++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, Mil_CDSN_IO); ++ WriteDOC(datum, docptr, WritePipeTerm); ++} ++ ++static u_char doc2001_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ //ReadDOC(docptr, CDSNSlowIO); ++ /* 11.4.5 -- delay twice to allow extended length cycle */ ++ DoC_Delay(doc, 2); ++ ReadDOC(docptr, ReadPipeInit); ++ //return ReadDOC(docptr, Mil_CDSN_IO); ++ return ReadDOC(docptr, LastDataRead); ++} ++ ++static void doc2001_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ /* Terminate write pipeline */ ++ WriteDOC(0x00, docptr, WritePipeTerm); ++} ++ ++static void doc2001_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); ++ ++ /* Terminate read pipeline */ ++ buf[i] = ReadDOC(docptr, LastDataRead); ++} ++ ++static int doc2001_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ++ ReadDOC(docptr, LastDataRead); ++ return i; ++ } ++ if (buf[i] != ReadDOC(docptr, LastDataRead)) ++ return i; ++ return 0; ++} ++ ++static u_char doc2001plus_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ u_char ret; ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ret = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug) printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++ ++static void doc2001plus_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("writebuf of %d bytes: ", len); ++ for (i=0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) printk("\n"); ++} ++ ++static void doc2001plus_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("readbuf of %d bytes: ", len); ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ for (i=0; i < len-2; i++) { ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ ++ /* Terminate read pipeline */ ++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug && i < 16) ++ printk("%02x ", buf[len-2]); ++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug && i < 16) ++ printk("%02x ", buf[len-1]); ++ if (debug) printk("\n"); ++} ++ ++static int doc2001plus_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("verifybuf of %d bytes: ", len); ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ for (i=0; i < len-2; i++) ++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ++ ReadDOC(docptr, Mplus_LastDataRead); ++ ReadDOC(docptr, Mplus_LastDataRead); ++ return i; ++ } ++ if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead)) ++ return len-2; ++ if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead)) ++ return len-1; ++ return 0; ++} ++ ++static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int floor = 0; ++ ++ if(debug)printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) { ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ return; ++ } ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ /* Assert ChipEnable and deassert WriteProtect */ ++ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); ++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++static void doc200x_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int floor = 0; ++ ++ if(debug)printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) ++ return; ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ /* 11.4.4 -- deassert CE before changing chip */ ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); ++ ++ WriteDOC(floor, docptr, FloorSelect); ++ WriteDOC(chip, docptr, CDSNDeviceSelect); ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ switch(cmd) { ++ case NAND_CTL_SETNCE: ++ doc->CDSNControl |= CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ doc->CDSNControl &= ~CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_SETCLE: ++ doc->CDSNControl |= CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ doc->CDSNControl &= ~CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ doc->CDSNControl |= CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ doc->CDSNControl &= ~CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_SETWP: ++ doc->CDSNControl |= CDSN_CTRL_WP; ++ break; ++ case NAND_CTL_CLRWP: ++ doc->CDSNControl &= ~CDSN_CTRL_WP; ++ break; ++ } ++ if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ /* 11.4.3 -- 4 NOPs after CSDNControl write */ ++ DoC_Delay(doc, 4); ++} ++ ++static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ /* ++ * Must terminate write pipeline before sending any commands ++ * to the device. ++ */ ++ if (command == NAND_CMD_PAGEPROG) { ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ } ++ ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ WriteDOC(readcmd, docptr, Mplus_FlashCmd); ++ } ++ WriteDOC(command, docptr, Mplus_FlashCmd); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ ++ if (column != -1 || page_addr != -1) { ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ WriteDOC(column, docptr, Mplus_FlashAddress); ++ } ++ if (page_addr != -1) { ++ WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress); ++ WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); ++ /* One more address cycle for higher density devices */ ++ if (this->chipsize & 0x0c000000) { ++ WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); ++ printk("high density\n"); ++ } ++ } ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ /* deassert ALE */ ++ if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ udelay(this->chip_delay); ++ WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay (100); ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++static int doc200x_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { ++ if(debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ if (debug)printk("was ready\n"); ++ return 1; ++ } else { ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if(debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ /* 11.4.2 -- Must NOP twice if it's ready */ ++ DoC_Delay(doc, 2); ++ if (debug)printk("was ready\n"); ++ return 1; ++ } ++} ++ ++static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++{ ++ /* This is our last resort if we couldn't find or create a BBT. Just ++ pretend all blocks are good. */ ++ return 0; ++} ++ ++static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ /* Prime the ECC engine */ ++ switch(mode) { ++ case NAND_ECC_READ: ++ WriteDOC(DOC_ECC_RESET, docptr, ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, ECCConf); ++ break; ++ case NAND_ECC_WRITE: ++ WriteDOC(DOC_ECC_RESET, docptr, ECCConf); ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); ++ break; ++ } ++} ++ ++static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ ++ /* Prime the ECC engine */ ++ switch(mode) { ++ case NAND_ECC_READ: ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); ++ break; ++ case NAND_ECC_WRITE: ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); ++ break; ++ } ++} ++ ++/* This code is only called on write */ ++static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ unsigned char *ecc_code) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ int emptymatch = 1; ++ ++ /* flush the pipeline */ ++ if (DoC_is_2000(doc)) { ++ WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ } else if (DoC_is_MillenniumPlus(doc)) { ++ WriteDOC(0, docptr, Mplus_NOP); ++ WriteDOC(0, docptr, Mplus_NOP); ++ WriteDOC(0, docptr, Mplus_NOP); ++ } else { ++ WriteDOC(0, docptr, NOP); ++ WriteDOC(0, docptr, NOP); ++ WriteDOC(0, docptr, NOP); ++ } ++ ++ for (i = 0; i < 6; i++) { ++ if (DoC_is_MillenniumPlus(doc)) ++ ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); ++ else ++ ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); ++ if (ecc_code[i] != empty_write_ecc[i]) ++ emptymatch = 0; ++ } ++ if (DoC_is_MillenniumPlus(doc)) ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ else ++ WriteDOC(DOC_ECC_DIS, docptr, ECCConf); ++#if 0 ++ /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ ++ if (emptymatch) { ++ /* Note: this somewhat expensive test should not be triggered ++ often. It could be optimized away by examining the data in ++ the writebuf routine, and remembering the result. */ ++ for (i = 0; i < 512; i++) { ++ if (dat[i] == 0xff) continue; ++ emptymatch = 0; ++ break; ++ } ++ } ++ /* If emptymatch still =1, we do have an all-0xff data buffer. ++ Return all-0xff ecc value instead of the computed one, so ++ it'll look just like a freshly-erased page. */ ++ if (emptymatch) memset(ecc_code, 0xff, 6); ++#endif ++ return 0; ++} ++ ++static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) ++{ ++ int i, ret = 0; ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ void __iomem *docptr = doc->virtadr; ++ volatile u_char dummy; ++ int emptymatch = 1; ++ ++ /* flush the pipeline */ ++ if (DoC_is_2000(doc)) { ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ } else if (DoC_is_MillenniumPlus(doc)) { ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ } else { ++ dummy = ReadDOC(docptr, ECCConf); ++ dummy = ReadDOC(docptr, ECCConf); ++ dummy = ReadDOC(docptr, ECCConf); ++ } ++ ++ /* Error occured ? */ ++ if (dummy & 0x80) { ++ for (i = 0; i < 6; i++) { ++ if (DoC_is_MillenniumPlus(doc)) ++ calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); ++ else ++ calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); ++ if (calc_ecc[i] != empty_read_syndrome[i]) ++ emptymatch = 0; ++ } ++ /* If emptymatch=1, the read syndrome is consistent with an ++ all-0xff data and stored ecc block. Check the stored ecc. */ ++ if (emptymatch) { ++ for (i = 0; i < 6; i++) { ++ if (read_ecc[i] == 0xff) continue; ++ emptymatch = 0; ++ break; ++ } ++ } ++ /* If emptymatch still =1, check the data block. */ ++ if (emptymatch) { ++ /* Note: this somewhat expensive test should not be triggered ++ often. It could be optimized away by examining the data in ++ the readbuf routine, and remembering the result. */ ++ for (i = 0; i < 512; i++) { ++ if (dat[i] == 0xff) continue; ++ emptymatch = 0; ++ break; ++ } ++ } ++ /* If emptymatch still =1, this is almost certainly a freshly- ++ erased block, in which case the ECC will not come out right. ++ We'll suppress the error and tell the caller everything's ++ OK. Because it is. */ ++ if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); ++ if (ret > 0) ++ printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); ++ } ++ if (DoC_is_MillenniumPlus(doc)) ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ else ++ WriteDOC(DOC_ECC_DIS, docptr, ECCConf); ++ if (no_ecc_failures && (ret == -1)) { ++ printk(KERN_ERR "suppressing ECC failure\n"); ++ ret = 0; ++ } ++ return ret; ++} ++ ++//u_char mydatabuf[528]; ++ ++static struct nand_oobinfo doc200x_oobinfo = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 6, ++ .eccpos = {0, 1, 2, 3, 4, 5}, ++ .oobfree = { {8, 8} } ++}; ++ ++/* Find the (I)NFTL Media Header, and optionally also the mirror media header. ++ On sucessful return, buf will contain a copy of the media header for ++ further processing. id is the string to scan for, and will presumably be ++ either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media ++ header. The page #s of the found media headers are placed in mh0_page and ++ mh1_page in the DOC private structure. */ ++static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, ++ const char *id, int findmirror) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); ++ int ret; ++ size_t retlen; ++ ++ end = min(end, mtd->size); // paranoia ++ for (offs = 0; offs < end; offs += mtd->erasesize) { ++ ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); ++ if (retlen != mtd->oobblock) continue; ++ if (ret) { ++ printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", ++ offs); ++ } ++ if (memcmp(buf, id, 6)) continue; ++ printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); ++ if (doc->mh0_page == -1) { ++ doc->mh0_page = offs >> this->page_shift; ++ if (!findmirror) return 1; ++ continue; ++ } ++ doc->mh1_page = offs >> this->page_shift; ++ return 2; ++ } ++ if (doc->mh0_page == -1) { ++ printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); ++ return 0; ++ } ++ /* Only one mediaheader was found. We want buf to contain a ++ mediaheader on return, so we'll have to re-read the one we found. */ ++ offs = doc->mh0_page << this->page_shift; ++ ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); ++ if (retlen != mtd->oobblock) { ++ /* Insanity. Give up. */ ++ printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); ++ return 0; ++ } ++ return 1; ++} ++ ++static inline int __init nftl_partscan(struct mtd_info *mtd, ++ struct mtd_partition *parts) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ int ret = 0; ++ u_char *buf; ++ struct NFTLMediaHeader *mh; ++ const unsigned psize = 1 << this->page_shift; ++ unsigned blocks, maxblocks; ++ int offs, numheaders; ++ ++ buf = kmalloc(mtd->oobblock, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); ++ return 0; ++ } ++ if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; ++ mh = (struct NFTLMediaHeader *) buf; ++ ++ mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); ++ mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); ++ mh->FormattedSize = le32_to_cpu(mh->FormattedSize); ++ ++ printk(KERN_INFO " DataOrgID = %s\n" ++ " NumEraseUnits = %d\n" ++ " FirstPhysicalEUN = %d\n" ++ " FormattedSize = %d\n" ++ " UnitSizeFactor = %d\n", ++ mh->DataOrgID, mh->NumEraseUnits, ++ mh->FirstPhysicalEUN, mh->FormattedSize, ++ mh->UnitSizeFactor); ++ ++ blocks = mtd->size >> this->phys_erase_shift; ++ maxblocks = min(32768U, mtd->erasesize - psize); ++ ++ if (mh->UnitSizeFactor == 0x00) { ++ /* Auto-determine UnitSizeFactor. The constraints are: ++ - There can be at most 32768 virtual blocks. ++ - There can be at most (virtual block size - page size) ++ virtual blocks (because MediaHeader+BBT must fit in 1). ++ */ ++ mh->UnitSizeFactor = 0xff; ++ while (blocks > maxblocks) { ++ blocks >>= 1; ++ maxblocks = min(32768U, (maxblocks << 1) + psize); ++ mh->UnitSizeFactor--; ++ } ++ printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); ++ } ++ ++ /* NOTE: The lines below modify internal variables of the NAND and MTD ++ layers; variables with have already been configured by nand_scan. ++ Unfortunately, we didn't know before this point what these values ++ should be. Thus, this code is somewhat dependant on the exact ++ implementation of the NAND layer. */ ++ if (mh->UnitSizeFactor != 0xff) { ++ this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); ++ mtd->erasesize <<= (0xff - mh->UnitSizeFactor); ++ printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); ++ blocks = mtd->size >> this->bbt_erase_shift; ++ maxblocks = min(32768U, mtd->erasesize - psize); ++ } ++ ++ if (blocks > maxblocks) { ++ printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); ++ goto out; ++ } ++ ++ /* Skip past the media headers. */ ++ offs = max(doc->mh0_page, doc->mh1_page); ++ offs <<= this->page_shift; ++ offs += mtd->erasesize; ++ ++ parts[0].name = " DiskOnChip BDTL partition"; ++ parts[0].offset = offs; ++ parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; ++ ++ offs += parts[0].size; ++ if (offs < mtd->size) { ++ parts[1].name = " DiskOnChip Remainder partition"; ++ parts[1].offset = offs; ++ parts[1].size = mtd->size - offs; ++ ret = 2; ++ goto out; ++ } ++ ret = 1; ++out: ++ kfree(buf); ++ return ret; ++} ++ ++/* This is a stripped-down copy of the code in inftlmount.c */ ++static inline int __init inftl_partscan(struct mtd_info *mtd, ++ struct mtd_partition *parts) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ int ret = 0; ++ u_char *buf; ++ struct INFTLMediaHeader *mh; ++ struct INFTLPartition *ip; ++ int numparts = 0; ++ int blocks; ++ int vshift, lastvunit = 0; ++ int i; ++ int end = mtd->size; ++ ++ if (inftl_bbt_write) ++ end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); ++ ++ buf = kmalloc(mtd->oobblock, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); ++ return 0; ++ } ++ ++ if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; ++ doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); ++ mh = (struct INFTLMediaHeader *) buf; ++ ++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); ++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); ++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); ++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); ++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); ++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed); ++ ++ printk(KERN_INFO " bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplerBits = %d\n" ++ " FormatFlgs = %d\n" ++ " OsakVersion = %d.%d.%d.%d\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, mh->NoOfBootImageBlocks, ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->BlockMultiplierBits, mh->FormatFlags, ++ ((unsigned char *) &mh->OsakVersion)[0] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[1] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[3] & 0xf, ++ mh->PercentUsed); ++ ++ vshift = this->phys_erase_shift + mh->BlockMultiplierBits; ++ ++ blocks = mtd->size >> vshift; ++ if (blocks > 32768) { ++ printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); ++ goto out; ++ } ++ ++ blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); ++ if (inftl_bbt_write && (blocks > mtd->erasesize)) { ++ printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); ++ goto out; ++ } ++ ++ /* Scan the partitions */ ++ for (i = 0; (i < 4); i++) { ++ ip = &(mh->Partitions[i]); ++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits); ++ ip->firstUnit = le32_to_cpu(ip->firstUnit); ++ ip->lastUnit = le32_to_cpu(ip->lastUnit); ++ ip->flags = le32_to_cpu(ip->flags); ++ ip->spareUnits = le32_to_cpu(ip->spareUnits); ++ ip->Reserved0 = le32_to_cpu(ip->Reserved0); ++ ++ printk(KERN_INFO " PARTITION[%d] ->\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ i, ip->virtualUnits, ip->firstUnit, ++ ip->lastUnit, ip->flags, ++ ip->spareUnits); ++ ++#if 0 ++ if ((i == 0) && (ip->firstUnit > 0)) { ++ parts[0].name = " DiskOnChip IPL / Media Header partition"; ++ parts[0].offset = 0; ++ parts[0].size = mtd->erasesize * ip->firstUnit; ++ numparts = 1; ++ } ++#endif ++ ++ if (ip->flags & INFTL_BINARY) ++ parts[numparts].name = " DiskOnChip BDK partition"; ++ else ++ parts[numparts].name = " DiskOnChip BDTL partition"; ++ parts[numparts].offset = ip->firstUnit << vshift; ++ parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; ++ numparts++; ++ if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; ++ if (ip->flags & INFTL_LAST) break; ++ } ++ lastvunit++; ++ if ((lastvunit << vshift) < end) { ++ parts[numparts].name = " DiskOnChip Remainder partition"; ++ parts[numparts].offset = lastvunit << vshift; ++ parts[numparts].size = end - parts[numparts].offset; ++ numparts++; ++ } ++ ret = numparts; ++out: ++ kfree(buf); ++ return ret; ++} ++ ++static int __init nftl_scan_bbt(struct mtd_info *mtd) ++{ ++ int ret, numparts; ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ struct mtd_partition parts[2]; ++ ++ memset((char *) parts, 0, sizeof(parts)); ++ /* On NFTL, we have to find the media headers before we can read the ++ BBTs, since they're stored in the media header eraseblocks. */ ++ numparts = nftl_partscan(mtd, parts); ++ if (!numparts) return -EIO; ++ this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | ++ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | ++ NAND_BBT_VERSION; ++ this->bbt_td->veroffs = 7; ++ this->bbt_td->pages[0] = doc->mh0_page + 1; ++ if (doc->mh1_page != -1) { ++ this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | ++ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | ++ NAND_BBT_VERSION; ++ this->bbt_md->veroffs = 7; ++ this->bbt_md->pages[0] = doc->mh1_page + 1; ++ } else { ++ this->bbt_md = NULL; ++ } ++ ++ /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. ++ At least as nand_bbt.c is currently written. */ ++ if ((ret = nand_scan_bbt(mtd, NULL))) ++ return ret; ++ add_mtd_device(mtd); ++#ifdef CONFIG_MTD_PARTITIONS ++ if (!no_autopart) ++ add_mtd_partitions(mtd, parts, numparts); ++#endif ++ return 0; ++} ++ ++static int __init inftl_scan_bbt(struct mtd_info *mtd) ++{ ++ int ret, numparts; ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ struct mtd_partition parts[5]; ++ ++ if (this->numchips > doc->chips_per_floor) { ++ printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); ++ return -EIO; ++ } ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; ++ if (inftl_bbt_write) ++ this->bbt_td->options |= NAND_BBT_WRITE; ++ this->bbt_td->pages[0] = 2; ++ this->bbt_md = NULL; ++ } else { ++ this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | ++ NAND_BBT_VERSION; ++ if (inftl_bbt_write) ++ this->bbt_td->options |= NAND_BBT_WRITE; ++ this->bbt_td->offs = 8; ++ this->bbt_td->len = 8; ++ this->bbt_td->veroffs = 7; ++ this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; ++ this->bbt_td->reserved_block_code = 0x01; ++ this->bbt_td->pattern = "MSYS_BBT"; ++ ++ this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | ++ NAND_BBT_VERSION; ++ if (inftl_bbt_write) ++ this->bbt_md->options |= NAND_BBT_WRITE; ++ this->bbt_md->offs = 8; ++ this->bbt_md->len = 8; ++ this->bbt_md->veroffs = 7; ++ this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; ++ this->bbt_md->reserved_block_code = 0x01; ++ this->bbt_md->pattern = "TBB_SYSM"; ++ } ++ ++ /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. ++ At least as nand_bbt.c is currently written. */ ++ if ((ret = nand_scan_bbt(mtd, NULL))) ++ return ret; ++ memset((char *) parts, 0, sizeof(parts)); ++ numparts = inftl_partscan(mtd, parts); ++ /* At least for now, require the INFTL Media Header. We could probably ++ do without it for non-INFTL use, since all it gives us is ++ autopartitioning, but I want to give it more thought. */ ++ if (!numparts) return -EIO; ++ add_mtd_device(mtd); ++#ifdef CONFIG_MTD_PARTITIONS ++ if (!no_autopart) ++ add_mtd_partitions(mtd, parts, numparts); ++#endif ++ return 0; ++} ++ ++static inline int __init doc2000_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ ++ this->write_byte = doc2000_write_byte; ++ this->read_byte = doc2000_read_byte; ++ this->write_buf = doc2000_writebuf; ++ this->read_buf = doc2000_readbuf; ++ this->verify_buf = doc2000_verifybuf; ++ this->scan_bbt = nftl_scan_bbt; ++ ++ doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; ++ doc2000_count_chips(mtd); ++ mtd->name = "DiskOnChip 2000 (NFTL Model)"; ++ return (4 * doc->chips_per_floor); ++} ++ ++static inline int __init doc2001_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ ++ this->write_byte = doc2001_write_byte; ++ this->read_byte = doc2001_read_byte; ++ this->write_buf = doc2001_writebuf; ++ this->read_buf = doc2001_readbuf; ++ this->verify_buf = doc2001_verifybuf; ++ ++ ReadDOC(doc->virtadr, ChipID); ++ ReadDOC(doc->virtadr, ChipID); ++ ReadDOC(doc->virtadr, ChipID); ++ if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { ++ /* It's not a Millennium; it's one of the newer ++ DiskOnChip 2000 units with a similar ASIC. ++ Treat it like a Millennium, except that it ++ can have multiple chips. */ ++ doc2000_count_chips(mtd); ++ mtd->name = "DiskOnChip 2000 (INFTL Model)"; ++ this->scan_bbt = inftl_scan_bbt; ++ return (4 * doc->chips_per_floor); ++ } else { ++ /* Bog-standard Millennium */ ++ doc->chips_per_floor = 1; ++ mtd->name = "DiskOnChip Millennium"; ++ this->scan_bbt = nftl_scan_bbt; ++ return 1; ++ } ++} ++ ++static inline int __init doc2001plus_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = this->priv; ++ ++ this->write_byte = NULL; ++ this->read_byte = doc2001plus_read_byte; ++ this->write_buf = doc2001plus_writebuf; ++ this->read_buf = doc2001plus_readbuf; ++ this->verify_buf = doc2001plus_verifybuf; ++ this->scan_bbt = inftl_scan_bbt; ++ this->hwcontrol = NULL; ++ this->select_chip = doc2001plus_select_chip; ++ this->cmdfunc = doc2001plus_command; ++ this->enable_hwecc = doc2001plus_enable_hwecc; ++ ++ doc->chips_per_floor = 1; ++ mtd->name = "DiskOnChip Millennium Plus"; ++ ++ return 1; ++} ++ ++static inline int __init doc_probe(unsigned long physadr) ++{ ++ unsigned char ChipID; ++ struct mtd_info *mtd; ++ struct nand_chip *nand; ++ struct doc_priv *doc; ++ void __iomem *virtadr; ++ unsigned char save_control; ++ unsigned char tmp, tmpb, tmpc; ++ int reg, len, numchips; ++ int ret = 0; ++ ++ virtadr = ioremap(physadr, DOC_IOREMAP_LEN); ++ if (!virtadr) { ++ printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); ++ return -EIO; ++ } ++ ++ /* It's not possible to cleanly detect the DiskOnChip - the ++ * bootup procedure will put the device into reset mode, and ++ * it's not possible to talk to it without actually writing ++ * to the DOCControl register. So we store the current contents ++ * of the DOCControl register's location, in case we later decide ++ * that it's not a DiskOnChip, and want to put it back how we ++ * found it. ++ */ ++ save_control = ReadDOC(virtadr, DOCControl); ++ ++ /* Reset the DiskOnChip ASIC */ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ virtadr, DOCControl); ++ ++ /* Enable the DiskOnChip ASIC */ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ virtadr, DOCControl); ++ ++ ChipID = ReadDOC(virtadr, ChipID); ++ ++ switch(ChipID) { ++ case DOC_ChipID_Doc2k: ++ reg = DoC_2k_ECCStatus; ++ break; ++ case DOC_ChipID_DocMil: ++ reg = DoC_ECCConf; ++ break; ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ case 0: ++ /* Possible Millennium Plus, need to do more checks */ ++ /* Possibly release from power down mode */ ++ for (tmp = 0; (tmp < 4); tmp++) ++ ReadDOC(virtadr, Mplus_Power); ++ ++ /* Reset the Millennium Plus ASIC */ ++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, virtadr, Mplus_DOCControl); ++ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); ++ ++ mdelay(1); ++ /* Enable the Millennium Plus ASIC */ ++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, virtadr, Mplus_DOCControl); ++ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); ++ mdelay(1); ++ ++ ChipID = ReadDOC(virtadr, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_DocMilPlus16: ++ reg = DoC_Mplus_Toggle; ++ break; ++ case DOC_ChipID_DocMilPlus32: ++ printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); ++ default: ++ ret = -ENODEV; ++ goto notfound; ++ } ++ break; ++ ++ default: ++ ret = -ENODEV; ++ goto notfound; ++ } ++ /* Check the TOGGLE bit in the ECC register */ ++ tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ if ((tmp == tmpb) || (tmp != tmpc)) { ++ printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); ++ ret = -ENODEV; ++ goto notfound; ++ } ++ ++ for (mtd = doclist; mtd; mtd = doc->nextdoc) { ++ unsigned char oldval; ++ unsigned char newval; ++ nand = mtd->priv; ++ doc = nand->priv; ++ /* Use the alias resolution register to determine if this is ++ in fact the same DOC aliased to a new address. If writes ++ to one chip's alias resolution register change the value on ++ the other chip, they're the same chip. */ ++ if (ChipID == DOC_ChipID_DocMilPlus16) { ++ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); ++ newval = ReadDOC(virtadr, Mplus_AliasResolution); ++ } else { ++ oldval = ReadDOC(doc->virtadr, AliasResolution); ++ newval = ReadDOC(virtadr, AliasResolution); ++ } ++ if (oldval != newval) ++ continue; ++ if (ChipID == DOC_ChipID_DocMilPlus16) { ++ WriteDOC(~newval, virtadr, Mplus_AliasResolution); ++ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); ++ WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it ++ } else { ++ WriteDOC(~newval, virtadr, AliasResolution); ++ oldval = ReadDOC(doc->virtadr, AliasResolution); ++ WriteDOC(newval, virtadr, AliasResolution); // restore it ++ } ++ newval = ~newval; ++ if (oldval == newval) { ++ printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); ++ goto notfound; ++ } ++ } ++ ++ printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); ++ ++ len = sizeof(struct mtd_info) + ++ sizeof(struct nand_chip) + ++ sizeof(struct doc_priv) + ++ (2 * sizeof(struct nand_bbt_descr)); ++ mtd = kmalloc(len, GFP_KERNEL); ++ if (!mtd) { ++ printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ memset(mtd, 0, len); ++ ++ nand = (struct nand_chip *) (mtd + 1); ++ doc = (struct doc_priv *) (nand + 1); ++ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); ++ nand->bbt_md = nand->bbt_td + 1; ++ ++ mtd->priv = nand; ++ mtd->owner = THIS_MODULE; ++ ++ nand->priv = doc; ++ nand->select_chip = doc200x_select_chip; ++ nand->hwcontrol = doc200x_hwcontrol; ++ nand->dev_ready = doc200x_dev_ready; ++ nand->waitfunc = doc200x_wait; ++ nand->block_bad = doc200x_block_bad; ++ nand->enable_hwecc = doc200x_enable_hwecc; ++ nand->calculate_ecc = doc200x_calculate_ecc; ++ nand->correct_data = doc200x_correct_data; ++ ++ nand->autooob = &doc200x_oobinfo; ++ nand->eccmode = NAND_ECC_HW6_512; ++ nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; ++ ++ doc->physadr = physadr; ++ doc->virtadr = virtadr; ++ doc->ChipID = ChipID; ++ doc->curfloor = -1; ++ doc->curchip = -1; ++ doc->mh0_page = -1; ++ doc->mh1_page = -1; ++ doc->nextdoc = doclist; ++ ++ if (ChipID == DOC_ChipID_Doc2k) ++ numchips = doc2000_init(mtd); ++ else if (ChipID == DOC_ChipID_DocMilPlus16) ++ numchips = doc2001plus_init(mtd); ++ else ++ numchips = doc2001_init(mtd); ++ ++ if ((ret = nand_scan(mtd, numchips))) { ++ /* DBB note: i believe nand_release is necessary here, as ++ buffers may have been allocated in nand_base. Check with ++ Thomas. FIX ME! */ ++ /* nand_release will call del_mtd_device, but we haven't yet ++ added it. This is handled without incident by ++ del_mtd_device, as far as I can tell. */ ++ nand_release(mtd); ++ kfree(mtd); ++ goto fail; ++ } ++ ++ /* Success! */ ++ doclist = mtd; ++ return 0; ++ ++notfound: ++ /* Put back the contents of the DOCControl register, in case it's not ++ actually a DiskOnChip. */ ++ WriteDOC(save_control, virtadr, DOCControl); ++fail: ++ iounmap(virtadr); ++ return ret; ++} ++ ++static void release_nanddoc(void) ++{ ++ struct mtd_info *mtd, *nextmtd; ++ struct nand_chip *nand; ++ struct doc_priv *doc; ++ ++ for (mtd = doclist; mtd; mtd = nextmtd) { ++ nand = mtd->priv; ++ doc = nand->priv; ++ ++ nextmtd = doc->nextdoc; ++ nand_release(mtd); ++ iounmap(doc->virtadr); ++ kfree(mtd); ++ } ++} ++ ++static int __init init_nanddoc(void) ++{ ++ int i, ret = 0; ++ ++ /* We could create the decoder on demand, if memory is a concern. ++ * This way we have it handy, if an error happens ++ * ++ * Symbolsize is 10 (bits) ++ * Primitve polynomial is x^10+x^3+1 ++ * first consecutive root is 510 ++ * primitve element to generate roots = 1 ++ * generator polinomial degree = 4 ++ */ ++ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); ++ if (!rs_decoder) { ++ printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); ++ return -ENOMEM; ++ } ++ ++ if (doc_config_location) { ++ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); ++ ret = doc_probe(doc_config_location); ++ if (ret < 0) ++ goto outerr; ++ } else { ++ for (i=0; (doc_locations[i] != 0xffffffff); i++) { ++ doc_probe(doc_locations[i]); ++ } ++ } ++ /* No banner message any more. Print a message if no DiskOnChip ++ found, so the user knows we at least tried. */ ++ if (!doclist) { ++ printk(KERN_INFO "No valid DiskOnChip devices found\n"); ++ ret = -ENODEV; ++ goto outerr; ++ } ++ return 0; ++outerr: ++ free_rs(rs_decoder); ++ return ret; ++} ++ ++static void __exit cleanup_nanddoc(void) ++{ ++ /* Cleanup the nand/DoC resources */ ++ release_nanddoc(); ++ ++ /* Free the reed solomon resources */ ++ if (rs_decoder) { ++ free_rs(rs_decoder); ++ } ++} ++ ++module_init(init_nanddoc); ++module_exit(cleanup_nanddoc); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); +--- linux-2.4.21/drivers/mtd/nand/edb7312.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/edb7312.c +@@ -6,7 +6,7 @@ + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -20,6 +20,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -52,41 +53,28 @@ + * Module stuff + */ + +-static int ep7312_fio_pbase = EP7312_FIO_PBASE; +-static int ep7312_pxdr = EP7312_PXDR; +-static int ep7312_pxddr = EP7312_PXDDR; +- +-#ifdef MODULE +-MODULE_PARM(ep7312_fio_pbase, "i"); +-MODULE_PARM(ep7312_pxdr, "i"); +-MODULE_PARM(ep7312_pxddr, "i"); +- +-__setup("ep7312_fio_pbase=",ep7312_fio_pbase); +-__setup("ep7312_pxdr=",ep7312_pxdr); +-__setup("ep7312_pxddr=",ep7312_pxddr); +-#endif ++static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE; ++static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR; ++static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR; + + #ifdef CONFIG_MTD_PARTITIONS + /* + * Define static partitions for flash device + */ + static struct mtd_partition partition_info[] = { +- { name: "EP7312 Nand Flash", +- offset: 0, +- size: 8*1024*1024 } ++ { .name = "EP7312 Nand Flash", ++ .offset = 0, ++ .size = 8*1024*1024 } + }; + #define NUM_PARTITIONS 1 + +-extern int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); + #endif + + + /* + * hardware specific access to control-lines + */ +-static void ep7312_hwcontrol(int cmd) ++static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) + { + switch(cmd) { + +@@ -116,10 +104,13 @@ + /* + * read device ready pin + */ +-static int ep7312_device_ready(void) ++static int ep7312_device_ready(struct mtd_info *mtd) + { + return 1; + } ++#ifdef CONFIG_MTD_PARTITIONS ++const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif + + /* + * Main initialization routine +@@ -130,7 +121,7 @@ + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; +- int ep7312_fio_base; ++ void __iomem * ep7312_fio_base; + + /* Allocate memory for MTD device structure and private data */ + ep7312_mtd = kmalloc(sizeof(struct mtd_info) + +@@ -142,7 +133,7 @@ + } + + /* map physical adress */ +- ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K); ++ ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K); + if(!ep7312_fio_base) { + printk("ioremap EDB7312 NAND flash failed\n"); + kfree(ep7312_mtd); +@@ -174,42 +165,22 @@ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (ep7312_mtd)) { ++ if (nand_scan (ep7312_mtd, 1)) { + iounmap((void *)ep7312_fio_base); + kfree (ep7312_mtd); + return -ENXIO; + } + +- /* Allocate memory for internal data buffer */ +- this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); +- if (!this->data_buf) { +- printk("Unable to allocate NAND data buffer for EDB7312.\n"); +- iounmap((void *)ep7312_fio_base); +- kfree (ep7312_mtd); +- return -ENOMEM; +- } +- +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk("Unable to allocate NAND data cache for EDB7312.\n"); +- kfree (this->data_buf); +- iounmap((void *)ep7312_fio_base); +- kfree (ep7312_mtd); +- return -ENOMEM; +- } +- this->cache_page = -1; +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, +- "edb7312-nand"); ++#ifdef CONFIG_MTD_PARTITIONS ++ ep7312_mtd->name = "edb7312-nand"; ++ mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, ++ &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; + #endif +- if (mtd_parts_nb == 0) +- { ++ if (mtd_parts_nb == 0) { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; +@@ -231,12 +202,11 @@ + { + struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1]; + +- /* Unregister the device */ +- del_mtd_device (ep7312_mtd); ++ /* Release resources, unregister device */ ++ nand_release (ap7312_mtd); + + /* Free internal data buffer */ + kfree (this->data_buf); +- kfree (this->data_cache); + + /* Free the MTD device structure */ + kfree (ep7312_mtd); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/h1910.c +@@ -0,0 +1,208 @@ ++/* ++ * drivers/mtd/nand/h1910.c ++ * ++ * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) ++ * ++ * Derived from drivers/mtd/nand/edb7312.c ++ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is ++ * a 128Mibit (16MiB x 8 bits) NAND flash device. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for CLPS7111_VIRT_BASE */ ++#include ++#include ++#include ++ ++/* ++ * MTD structure for EDB7312 board ++ */ ++static struct mtd_info *h1910_nand_mtd = NULL; ++ ++/* ++ * Module stuff ++ */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++/* ++ * Define static partitions for flash device ++ */ ++static struct mtd_partition partition_info[] = { ++ { name: "h1910 NAND Flash", ++ offset: 0, ++ size: 16*1024*1024 } ++}; ++#define NUM_PARTITIONS 1 ++ ++#endif ++ ++ ++/* ++ * hardware specific access to control-lines ++ */ ++static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip* this = (struct nand_chip *) (mtd->priv); ++ ++ switch(cmd) { ++ ++ case NAND_CTL_SETCLE: ++ this->IO_ADDR_R |= (1 << 2); ++ this->IO_ADDR_W |= (1 << 2); ++ break; ++ case NAND_CTL_CLRCLE: ++ this->IO_ADDR_R &= ~(1 << 2); ++ this->IO_ADDR_W &= ~(1 << 2); ++ break; ++ ++ case NAND_CTL_SETALE: ++ this->IO_ADDR_R |= (1 << 3); ++ this->IO_ADDR_W |= (1 << 3); ++ break; ++ case NAND_CTL_CLRALE: ++ this->IO_ADDR_R &= ~(1 << 3); ++ this->IO_ADDR_W &= ~(1 << 3); ++ break; ++ ++ case NAND_CTL_SETNCE: ++ break; ++ case NAND_CTL_CLRNCE: ++ break; ++ } ++} ++ ++/* ++ * read device ready pin ++ */ ++#if 0 ++static int h1910_device_ready(struct mtd_info *mtd) ++{ ++ return (GPLR(55) & GPIO_bit(55)); ++} ++#endif ++ ++/* ++ * Main initialization routine ++ */ ++static int __init h1910_init (void) ++{ ++ struct nand_chip *this; ++ const char *part_type = 0; ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ void __iomem *nandaddr; ++ ++ if (!machine_is_h1900()) ++ return -ENODEV; ++ ++ nandaddr = __ioremap(0x08000000, 0x1000, 0, 1); ++ if (!nandaddr) { ++ printk("Failed to ioremap nand flash.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Allocate memory for MTD device structure and private data */ ++ h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + ++ sizeof(struct nand_chip), ++ GFP_KERNEL); ++ if (!h1910_nand_mtd) { ++ printk("Unable to allocate h1910 NAND MTD device structure.\n"); ++ iounmap ((void *) nandaddr); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&h1910_nand_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ h1910_nand_mtd->priv = this; ++ ++ /* ++ * Enable VPEN ++ */ ++ GPSR(37) = GPIO_bit(37); ++ ++ /* insert callbacks */ ++ this->IO_ADDR_R = nandaddr; ++ this->IO_ADDR_W = nandaddr; ++ this->hwcontrol = h1910_hwcontrol; ++ this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ ++ /* 15 us command delay time */ ++ this->chip_delay = 50; ++ this->eccmode = NAND_ECC_SOFT; ++ this->options = NAND_NO_AUTOINCR; ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (h1910_nand_mtd, 1)) { ++ printk(KERN_NOTICE "No NAND device - returning -ENXIO\n"); ++ kfree (h1910_nand_mtd); ++ iounmap ((void *) nandaddr); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, ++ "h1910-nand"); ++ if (mtd_parts_nb > 0) ++ part_type = "command line"; ++ else ++ mtd_parts_nb = 0; ++#endif ++ if (mtd_parts_nb == 0) ++ { ++ mtd_parts = partition_info; ++ mtd_parts_nb = NUM_PARTITIONS; ++ part_type = "static"; ++ } ++ ++ /* Register the partitions */ ++ printk(KERN_NOTICE "Using %s partition definition\n", part_type); ++ add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); ++ ++ /* Return happy */ ++ return 0; ++} ++module_init(h1910_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit h1910_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1]; ++ ++ /* Release resources, unregister device */ ++ nand_release (h1910_nand_mtd); ++ ++ /* Release io resource */ ++ iounmap ((void *) this->IO_ADDR_W); ++ ++ /* Free the MTD device structure */ ++ kfree (h1910_nand_mtd); ++} ++module_exit(h1910_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Joshua Wise "); ++MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/nand_base.c +@@ -0,0 +1,2691 @@ ++/* ++ * drivers/mtd/nand.c ++ * ++ * Overview: ++ * This is the generic MTD driver for NAND flash devices. It should be ++ * capable of working with almost all NAND chips currently available. ++ * Basic support for AG-AND chips is provided. ++ * ++ * Additional technical information is available on ++ * http://www.linux-mtd.infradead.org/tech/nand.html ++ * ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) ++ * 2002 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * 02-08-2004 tglx: support for strange chips, which cannot auto increment ++ * pages on read / read_oob ++ * ++ * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes ++ * pointed this out, as he marked an auto increment capable chip ++ * as NOAUTOINCR in the board driver. ++ * Make reads over block boundaries work too ++ * ++ * 04-14-2004 tglx: first working version for 2k page size chips ++ * ++ * 05-19-2004 tglx: Basic support for Renesas AG-AND chips ++ * ++ * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared ++ * among multiple independend devices. Suggestions and initial patch ++ * from Ben Dooks ++ * ++ * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. ++ * Basically, any block not rewritten may lose data when surrounding blocks ++ * are rewritten many times. JFFS2 ensures this doesn't happen for blocks ++ * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they ++ * do not lose data, force them to be rewritten when some of the surrounding ++ * blocks are erased. Rather than tracking a specific nearby block (which ++ * could itself go bad), use a page address 'mask' to select several blocks ++ * in the same area, and rewrite the BBT when any of them are erased. ++ * ++ * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas ++ * AG-AND chips. If there was a sudden loss of power during an erase operation, ++ * a "device recovery" operation must be performed when power is restored ++ * to ensure correct operation. ++ * ++ * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to ++ * perform extra error status checks on erase and write failures. This required ++ * adding a wrapper function for nand_read_ecc. ++ * ++ * Credits: ++ * David Woodhouse for adding multichip support ++ * ++ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the ++ * rework for 2K page size chips ++ * ++ * TODO: ++ * Enable cached programming for 2k page size chips ++ * Check, if mtd->ecctype should be set to MTD_ECC_HW ++ * if we have HW ecc support. ++ * The AG-AND chips have nice features for speed improvement, ++ * which are not supported yet. Read / program 4 pages in one go. ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#include ++#endif ++ ++/* Define default oob placement schemes for large and small page devices */ ++static struct nand_oobinfo nand_oob_8 = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 3, ++ .eccpos = {0, 1, 2}, ++ .oobfree = { {3, 2}, {6, 2} } ++}; ++ ++static struct nand_oobinfo nand_oob_16 = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 6, ++ .eccpos = {0, 1, 2, 3, 6, 7}, ++ .oobfree = { {8, 8} } ++}; ++ ++static struct nand_oobinfo nand_oob_64 = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 24, ++ .eccpos = { ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63}, ++ .oobfree = { {2, 38} } ++}; ++ ++/* This is used for padding purposes in nand_write_oob */ ++static u_char ffchars[] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++}; ++ ++/* ++ * NAND low-level MTD interface functions ++ */ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); ++ ++static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); ++static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); ++static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); ++static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); ++static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); ++static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); ++static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, ++ unsigned long count, loff_t to, size_t * retlen); ++static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, ++ unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); ++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); ++static void nand_sync (struct mtd_info *mtd); ++ ++/* Some internal functions */ ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, ++ struct nand_oobinfo *oobsel, int mode); ++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE ++static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, ++ u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); ++#else ++#define nand_verify_pages(...) (0) ++#endif ++ ++static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); ++ ++/** ++ * nand_release_device - [GENERIC] release chip ++ * @mtd: MTD device structure ++ * ++ * Deselect, release chip lock and wake up anyone waiting on the device ++ */ ++static void nand_release_device (struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ /* De-select the NAND device */ ++ this->select_chip(mtd, -1); ++ /* Do we have a hardware controller ? */ ++ if (this->controller) { ++ spin_lock(&this->controller->lock); ++ this->controller->active = NULL; ++ spin_unlock(&this->controller->lock); ++ } ++ /* Release the chip */ ++ spin_lock (&this->chip_lock); ++ this->state = FL_READY; ++ wake_up (&this->wq); ++ spin_unlock (&this->chip_lock); ++} ++ ++/** ++ * nand_read_byte - [DEFAULT] read one byte from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 8bit buswith ++ */ ++static u_char nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return readb(this->IO_ADDR_R); ++} ++ ++/** ++ * nand_write_byte - [DEFAULT] write one byte to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * Default write function for 8it buswith ++ */ ++static void nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writeb(byte, this->IO_ADDR_W); ++} ++ ++/** ++ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 16bit buswith with ++ * endianess conversion ++ */ ++static u_char nand_read_byte16(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); ++} ++ ++/** ++ * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * Default write function for 16bit buswith with ++ * endianess conversion ++ */ ++static void nand_write_byte16(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); ++} ++ ++/** ++ * nand_read_word - [DEFAULT] read one word from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 16bit buswith without ++ * endianess conversion ++ */ ++static u16 nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return readw(this->IO_ADDR_R); ++} ++ ++/** ++ * nand_write_word - [DEFAULT] write one word to the chip ++ * @mtd: MTD device structure ++ * @word: data word to write ++ * ++ * Default write function for 16bit buswith without ++ * endianess conversion ++ */ ++static void nand_write_word(struct mtd_info *mtd, u16 word) ++{ ++ struct nand_chip *this = mtd->priv; ++ writew(word, this->IO_ADDR_W); ++} ++ ++/** ++ * nand_select_chip - [DEFAULT] control CE line ++ * @mtd: MTD device structure ++ * @chip: chipnumber to select, -1 for deselect ++ * ++ * Default select function for 1 chip devices. ++ */ ++static void nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ switch(chip) { ++ case -1: ++ this->hwcontrol(mtd, NAND_CTL_CLRNCE); ++ break; ++ case 0: ++ this->hwcontrol(mtd, NAND_CTL_SETNCE); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++/** ++ * nand_write_buf - [DEFAULT] write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * Default write function for 8bit buswith ++ */ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++/** ++ * nand_read_buf - [DEFAULT] read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * Default read function for 8bit buswith ++ */ ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++/** ++ * nand_verify_buf - [DEFAULT] Verify chip data against buffer ++ * @mtd: MTD device structure ++ * @buf: buffer containing the data to compare ++ * @len: number of bytes to compare ++ * ++ * Default verify function for 8bit buswith ++ */ ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/** ++ * nand_write_buf16 - [DEFAULT] write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * Default write function for 16bit buswith ++ */ ++static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_W); ++ ++} ++ ++/** ++ * nand_read_buf16 - [DEFAULT] read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * Default read function for 16bit buswith ++ */ ++static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++/** ++ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer ++ * @mtd: MTD device structure ++ * @buf: buffer containing the data to compare ++ * @len: number of bytes to compare ++ * ++ * Default verify function for 16bit buswith ++ */ ++static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/** ++ * nand_block_bad - [DEFAULT] Read bad block marker from the chip ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * @getchip: 0, if the chip is already selected ++ * ++ * Check, if the block is bad. ++ */ ++static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++{ ++ int page, chipnr, res = 0; ++ struct nand_chip *this = mtd->priv; ++ u16 bad; ++ ++ if (getchip) { ++ page = (int)(ofs >> this->page_shift); ++ chipnr = (int)(ofs >> this->chip_shift); ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_READING); ++ ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ } else ++ page = (int) ofs; ++ ++ if (this->options & NAND_BUSWIDTH_16) { ++ this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); ++ bad = cpu_to_le16(this->read_word(mtd)); ++ if (this->badblockpos & 0x1) ++ bad >>= 1; ++ if ((bad & 0xFF) != 0xff) ++ res = 1; ++ } else { ++ this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); ++ if (this->read_byte(mtd) != 0xff) ++ res = 1; ++ } ++ ++ if (getchip) { ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ } ++ ++ return res; ++} ++ ++/** ++ * nand_default_block_markbad - [DEFAULT] mark a block bad ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * This is the default implementation, which can be overridden by ++ * a hardware specific driver. ++*/ ++static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *this = mtd->priv; ++ u_char buf[2] = {0, 0}; ++ size_t retlen; ++ int block; ++ ++ /* Get block number */ ++ block = ((int) ofs) >> this->bbt_erase_shift; ++ if (this->bbt) ++ this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); ++ ++ /* Do we have a flash based bad block table ? */ ++ if (this->options & NAND_USE_FLASH_BBT) ++ return nand_update_bbt (mtd, ofs); ++ ++ /* We write two bytes, so we dont have to mess with 16 bit access */ ++ ofs += mtd->oobsize + (this->badblockpos & ~0x01); ++ return nand_write_oob (mtd, ofs , 2, &retlen, buf); ++} ++ ++/** ++ * nand_check_wp - [GENERIC] check if the chip is write protected ++ * @mtd: MTD device structure ++ * Check, if the device is write protected ++ * ++ * The function expects, that the device is already selected ++ */ ++static int nand_check_wp (struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ /* Check the WP bit */ ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; ++} ++ ++/** ++ * nand_block_checkbad - [GENERIC] Check if a block is marked bad ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * @getchip: 0, if the chip is already selected ++ * @allowbbt: 1, if its allowed to access the bbt area ++ * ++ * Check, if the block is bad. Either by reading the bad block table or ++ * calling of the scan function. ++ */ ++static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ if (!this->bbt) ++ return this->block_bad(mtd, ofs, getchip); ++ ++ /* Return info from the table */ ++ return nand_isbad_bbt (mtd, ofs, allowbbt); ++} ++ ++/* ++ * Wait for the ready pin, after a command ++ * The timeout is catched later. ++ */ ++static void nand_wait_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ unsigned long timeo = jiffies + 2; ++ ++ /* wait until command is processed or timeout occures */ ++ do { ++ if (this->dev_ready(mtd)) ++ return; ++ } while (time_before(jiffies, timeo)); ++} ++ ++/** ++ * nand_command - [DEFAULT] Send command to NAND device ++ * @mtd: MTD device structure ++ * @command: the command to be sent ++ * @column: the column address for this command, -1 if none ++ * @page_addr: the page address for this command, -1 if none ++ * ++ * Send command to NAND device. This function is used for small page ++ * devices (256/512 Bytes per page) ++ */ ++static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ this->write_byte(mtd, column); ++ } ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for devices > 32MiB */ ++ if (this->chipsize > (32 << 20)) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ udelay(this->chip_delay); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay (100); ++ ++ nand_wait_ready(mtd); ++} ++ ++/** ++ * nand_command_lp - [DEFAULT] Send command to NAND large page device ++ * @mtd: MTD device structure ++ * @command: the command to be sent ++ * @column: the column address for this command, -1 if none ++ * @page_addr: the page address for this command, -1 if none ++ * ++ * Send command to NAND device. This is the version for the new large page devices ++ * We dont have the seperate regions as we have in the small page devices. ++ * We must emulate NAND_CMD_READOOB to keep the code compatible. ++ * ++ */ ++static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Emulate NAND_CMD_READOOB */ ++ if (command == NAND_CMD_READOOB) { ++ column += mtd->oobblock; ++ command = NAND_CMD_READ0; ++ } ++ ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* Write out the command to the device. */ ++ this->write_byte(mtd, (command & 0xff)); ++ /* End command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ this->write_byte(mtd, column & 0xff); ++ this->write_byte(mtd, column >> 8); ++ } ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for devices > 128MiB */ ++ if (this->chipsize > (128 << 20)) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status, sequential in, and deplete1 need no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_CACHEDPROG: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_DEPLETE1: ++ return; ++ ++ /* ++ * read error status commands require only a short delay ++ */ ++ case NAND_CMD_STATUS_ERROR: ++ case NAND_CMD_STATUS_ERROR0: ++ case NAND_CMD_STATUS_ERROR1: ++ case NAND_CMD_STATUS_ERROR2: ++ case NAND_CMD_STATUS_ERROR3: ++ udelay(this->chip_delay); ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ udelay(this->chip_delay); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); ++ return; ++ ++ case NAND_CMD_READ0: ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* Write out the start read command */ ++ this->write_byte(mtd, NAND_CMD_READSTART); ++ /* End command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ /* Fall through into ready check */ ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay (100); ++ ++ nand_wait_ready(mtd); ++} ++ ++/** ++ * nand_get_device - [GENERIC] Get chip for selected access ++ * @this: the nand chip descriptor ++ * @mtd: MTD device structure ++ * @new_state: the state which is requested ++ * ++ * Get the device and lock it for exclusive access ++ */ ++static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) ++{ ++ struct nand_chip *active = this; ++ ++ DECLARE_WAITQUEUE (wait, current); ++ ++ /* ++ * Grab the lock and see if the device is available ++ */ ++retry: ++ /* Hardware controller shared among independend devices */ ++ if (this->controller) { ++ spin_lock (&this->controller->lock); ++ if (this->controller->active) ++ active = this->controller->active; ++ else ++ this->controller->active = this; ++ spin_unlock (&this->controller->lock); ++ } ++ ++ if (active == this) { ++ spin_lock (&this->chip_lock); ++ if (this->state == FL_READY) { ++ this->state = new_state; ++ spin_unlock (&this->chip_lock); ++ return; ++ } ++ } ++ set_current_state (TASK_UNINTERRUPTIBLE); ++ add_wait_queue (&active->wq, &wait); ++ spin_unlock (&active->chip_lock); ++ schedule (); ++ remove_wait_queue (&active->wq, &wait); ++ goto retry; ++} ++ ++/** ++ * nand_wait - [DEFAULT] wait until the command is done ++ * @mtd: MTD device structure ++ * @this: NAND chip structure ++ * @state: state to select the max. timeout value ++ * ++ * Wait for command done. This applies to erase and program only ++ * Erase can take up to 400ms and program up to 20ms according to ++ * general NAND and SmartMedia specs ++ * ++*/ ++static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) ++{ ++ ++ unsigned long timeo = jiffies; ++ int status; ++ ++ if (state == FL_ERASING) ++ timeo += (HZ * 400) / 1000; ++ else ++ timeo += (HZ * 20) / 1000; ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay (100); ++ ++ if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) ++ this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); ++ else ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ ++ while (time_before(jiffies, timeo)) { ++ /* Check, if we were interrupted */ ++ if (this->state != state) ++ return 0; ++ ++ if (this->dev_ready) { ++ if (this->dev_ready(mtd)) ++ break; ++ } else { ++ if (this->read_byte(mtd) & NAND_STATUS_READY) ++ break; ++ } ++ cond_resched(); ++ } ++ status = (int) this->read_byte(mtd); ++ return status; ++} ++ ++/** ++ * nand_write_page - [GENERIC] write one page ++ * @mtd: MTD device structure ++ * @this: NAND chip structure ++ * @page: startpage inside the chip, must be called with (page & this->pagemask) ++ * @oob_buf: out of band data buffer ++ * @oobsel: out of band selecttion structre ++ * @cached: 1 = enable cached programming if supported by chip ++ * ++ * Nand_page_program function is used for write and writev ! ++ * This function will always program a full page of data ++ * If you call it with a non page aligned buffer, you're lost :) ++ * ++ * Cached programming is not supported yet. ++ */ ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, ++ u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) ++{ ++ int i, status; ++ u_char ecc_code[32]; ++ int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ int *oob_config = oobsel->eccpos; ++ int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; ++ int eccbytes = 0; ++ ++ /* FIXME: Enable cached programming */ ++ cached = 0; ++ ++ /* Send command to begin auto page programming */ ++ this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); ++ ++ /* Write out complete page of data, take care of eccmode */ ++ switch (eccmode) { ++ /* No ecc, write all */ ++ case NAND_ECC_NONE: ++ printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ break; ++ ++ /* Software ecc 3/256, write all */ ++ case NAND_ECC_SOFT: ++ for (; eccsteps; eccsteps--) { ++ this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); ++ for (i = 0; i < 3; i++, eccidx++) ++ oob_buf[oob_config[eccidx]] = ecc_code[i]; ++ datidx += this->eccsize; ++ } ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ break; ++ default: ++ eccbytes = this->eccbytes; ++ for (; eccsteps; eccsteps--) { ++ /* enable hardware ecc logic for write */ ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); ++ this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); ++ this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); ++ for (i = 0; i < eccbytes; i++, eccidx++) ++ oob_buf[oob_config[eccidx]] = ecc_code[i]; ++ /* If the hardware ecc provides syndromes then ++ * the ecc code must be written immidiately after ++ * the data bytes (words) */ ++ if (this->options & NAND_HWECC_SYNDROME) ++ this->write_buf(mtd, ecc_code, eccbytes); ++ datidx += this->eccsize; ++ } ++ break; ++ } ++ ++ /* Write out OOB data */ ++ if (this->options & NAND_HWECC_SYNDROME) ++ this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); ++ else ++ this->write_buf(mtd, oob_buf, mtd->oobsize); ++ ++ /* Send command to actually program the data */ ++ this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); ++ ++ if (!cached) { ++ /* call wait ready function */ ++ status = this->waitfunc (mtd, this, FL_WRITING); ++ ++ /* See if operation failed and additional status checks are available */ ++ if ((status & NAND_STATUS_FAIL) && (this->errstat)) { ++ status = this->errstat(mtd, this, FL_WRITING, status, page); ++ } ++ ++ /* See if device thinks it succeeded */ ++ if (status & NAND_STATUS_FAIL) { ++ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); ++ return -EIO; ++ } ++ } else { ++ /* FIXME: Implement cached programming ! */ ++ /* wait until cache is ready*/ ++ // status = this->waitfunc (mtd, this, FL_CACHEDRPG); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE ++/** ++ * nand_verify_pages - [GENERIC] verify the chip contents after a write ++ * @mtd: MTD device structure ++ * @this: NAND chip structure ++ * @page: startpage inside the chip, must be called with (page & this->pagemask) ++ * @numpages: number of pages to verify ++ * @oob_buf: out of band data buffer ++ * @oobsel: out of band selecttion structre ++ * @chipnr: number of the current chip ++ * @oobmode: 1 = full buffer verify, 0 = ecc only ++ * ++ * The NAND device assumes that it is always writing to a cleanly erased page. ++ * Hence, it performs its internal write verification only on bits that ++ * transitioned from 1 to 0. The device does NOT verify the whole page on a ++ * byte by byte basis. It is possible that the page was not completely erased ++ * or the page is becoming unusable due to wear. The read with ECC would catch ++ * the error later when the ECC page check fails, but we would rather catch ++ * it early in the page write stage. Better to write no data than invalid data. ++ */ ++static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, ++ u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) ++{ ++ int i, j, datidx = 0, oobofs = 0, res = -EIO; ++ int eccsteps = this->eccsteps; ++ int hweccbytes; ++ u_char oobdata[64]; ++ ++ hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; ++ ++ /* Send command to read back the first page */ ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); ++ ++ for(;;) { ++ for (j = 0; j < eccsteps; j++) { ++ /* Loop through and verify the data */ ++ if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); ++ goto out; ++ } ++ datidx += mtd->eccsize; ++ /* Have we a hw generator layout ? */ ++ if (!hweccbytes) ++ continue; ++ if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); ++ goto out; ++ } ++ oobofs += hweccbytes; ++ } ++ ++ /* check, if we must compare all data or if we just have to ++ * compare the ecc bytes ++ */ ++ if (oobmode) { ++ if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); ++ goto out; ++ } ++ } else { ++ /* Read always, else autoincrement fails */ ++ this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); ++ ++ if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { ++ int ecccnt = oobsel->eccbytes; ++ ++ for (i = 0; i < ecccnt; i++) { ++ int idx = oobsel->eccpos[i]; ++ if (oobdata[idx] != oob_buf[oobofs + idx] ) { ++ DEBUG (MTD_DEBUG_LEVEL0, ++ "%s: Failed ECC write " ++ "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); ++ goto out; ++ } ++ } ++ } ++ } ++ oobofs += mtd->oobsize - hweccbytes * eccsteps; ++ page++; ++ numpages--; ++ ++ /* Apply delay or wait for ready/busy pin ++ * Do this before the AUTOINCR check, so no problems ++ * arise if a chip which does auto increment ++ * is marked as NOAUTOINCR by the board driver. ++ * Do this also before returning, so the chip is ++ * ready for the next command. ++ */ ++ if (!this->dev_ready) ++ udelay (this->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ ++ /* All done, return happy */ ++ if (!numpages) ++ return 0; ++ ++ ++ /* Check, if the chip supports auto page increment */ ++ if (!NAND_CANAUTOINCR(this)) ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); ++ } ++ /* ++ * Terminate the read command. We come here in case of an error ++ * So we must issue a reset command. ++ */ ++out: ++ this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); ++ return res; ++} ++#endif ++ ++/** ++ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @retlen: pointer to variable to store the number of read bytes ++ * @buf: the databuffer to put data ++ * ++ * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL ++ * and flags = 0xff ++ */ ++static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) ++{ ++ return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff); ++} ++ ++ ++/** ++ * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @retlen: pointer to variable to store the number of read bytes ++ * @buf: the databuffer to put data ++ * @oob_buf: filesystem supplied oob data buffer ++ * @oobsel: oob selection structure ++ * ++ * This function simply calls nand_do_read_ecc with flags = 0xff ++ */ ++static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) ++{ ++ return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); ++} ++ ++ ++/** ++ * nand_do_read_ecc - [MTD Interface] Read data with ECC ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @retlen: pointer to variable to store the number of read bytes ++ * @buf: the databuffer to put data ++ * @oob_buf: filesystem supplied oob data buffer ++ * @oobsel: oob selection structure ++ * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed ++ * and how many corrected error bits are acceptable: ++ * bits 0..7 - number of tolerable errors ++ * bit 8 - 0 == do not get/release chip, 1 == get/release chip ++ * ++ * NAND read with ECC ++ */ ++int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * oob_buf, ++ struct nand_oobinfo *oobsel, int flags) ++{ ++ int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; ++ int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; ++ struct nand_chip *this = mtd->priv; ++ u_char *data_poi, *oob_data = oob_buf; ++ u_char ecc_calc[32]; ++ u_char ecc_code[32]; ++ int eccmode, eccsteps; ++ int *oob_config, datidx; ++ int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; ++ int eccbytes; ++ int compareecc = 1; ++ int oobreadlen; ++ ++ ++ DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); ++ ++ /* Do not allow reads past end of device */ ++ if ((from + len) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); ++ *retlen = 0; ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ if (flags & NAND_GET_DEVICE) ++ nand_get_device (this, mtd, FL_READING); ++ ++ /* use userspace supplied oobinfo, if zero */ ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ ++ /* Autoplace of oob data ? Use the default placement scheme */ ++ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) ++ oobsel = this->autooob; ++ ++ eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ oob_config = oobsel->eccpos; ++ ++ /* Select the NAND device */ ++ chipnr = (int)(from >> this->chip_shift); ++ this->select_chip(mtd, chipnr); ++ ++ /* First we calculate the starting page */ ++ realpage = (int) (from >> this->page_shift); ++ page = realpage & this->pagemask; ++ ++ /* Get raw starting column */ ++ col = from & (mtd->oobblock - 1); ++ ++ end = mtd->oobblock; ++ ecc = this->eccsize; ++ eccbytes = this->eccbytes; ++ ++ if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) ++ compareecc = 0; ++ ++ oobreadlen = mtd->oobsize; ++ if (this->options & NAND_HWECC_SYNDROME) ++ oobreadlen -= oobsel->eccbytes; ++ ++ /* Loop until all data read */ ++ while (read < len) { ++ ++ int aligned = (!col && (len - read) >= end); ++ /* ++ * If the read is not page aligned, we have to read into data buffer ++ * due to ecc, else we read into return buffer direct ++ */ ++ if (aligned) ++ data_poi = &buf[read]; ++ else ++ data_poi = this->data_buf; ++ ++ /* Check, if we have this page in the buffer ++ * ++ * FIXME: Make it work when we must provide oob data too, ++ * check the usage of data_buf oob field ++ */ ++ if (realpage == this->pagebuf && !oob_buf) { ++ /* aligned read ? */ ++ if (aligned) ++ memcpy (data_poi, this->data_buf, end); ++ goto readdata; ++ } ++ ++ /* Check, if we must send the read command */ ++ if (sndcmd) { ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); ++ sndcmd = 0; ++ } ++ ++ /* get oob area, if we have no oob buffer from fs-driver */ ++ if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE) ++ oob_data = &this->data_buf[end]; ++ ++ eccsteps = this->eccsteps; ++ ++ switch (eccmode) { ++ case NAND_ECC_NONE: { /* No ECC, Read in a page */ ++ static unsigned long lastwhinge = 0; ++ if ((lastwhinge / HZ) != (jiffies / HZ)) { ++ printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); ++ lastwhinge = jiffies; ++ } ++ this->read_buf(mtd, data_poi, end); ++ break; ++ } ++ ++ case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ ++ this->read_buf(mtd, data_poi, end); ++ for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) ++ this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); ++ break; ++ ++ default: ++ for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, &data_poi[datidx], ecc); ++ ++ /* HW ecc with syndrome calculation must read the ++ * syndrome from flash immidiately after the data */ ++ if (!compareecc) { ++ /* Some hw ecc generators need to know when the ++ * syndrome is read from flash */ ++ this->enable_hwecc(mtd, NAND_ECC_READSYN); ++ this->read_buf(mtd, &oob_data[i], eccbytes); ++ /* We calc error correction directly, it checks the hw ++ * generator for an error, reads back the syndrome and ++ * does the error correction on the fly */ ++ ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); ++ if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " ++ "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ++ ecc_failed++; ++ } ++ } else { ++ this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); ++ } ++ } ++ break; ++ } ++ ++ /* read oobdata */ ++ this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); ++ ++ /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ ++ if (!compareecc) ++ goto readoob; ++ ++ /* Pick the ECC bytes out of the oob data */ ++ for (j = 0; j < oobsel->eccbytes; j++) ++ ecc_code[j] = oob_data[oob_config[j]]; ++ ++ /* correct data, if neccecary */ ++ for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { ++ ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); ++ ++ /* Get next chunk of ecc bytes */ ++ j += eccbytes; ++ ++ /* Check, if we have a fs supplied oob-buffer, ++ * This is the legacy mode. Used by YAFFS1 ++ * Should go away some day ++ */ ++ if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { ++ int *p = (int *)(&oob_data[mtd->oobsize]); ++ p[i] = ecc_status; ++ } ++ ++ if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ++ ecc_failed++; ++ } ++ } ++ ++ readoob: ++ /* check, if we have a fs supplied oob-buffer */ ++ if (oob_buf) { ++ /* without autoplace. Legacy mode used by YAFFS1 */ ++ switch(oobsel->useecc) { ++ case MTD_NANDECC_AUTOPLACE: ++ /* Walk through the autoplace chunks */ ++ for (i = 0, j = 0; j < mtd->oobavail; i++) { ++ int from = oobsel->oobfree[i][0]; ++ int num = oobsel->oobfree[i][1]; ++ memcpy(&oob_buf[oob], &oob_data[from], num); ++ j+= num; ++ } ++ oob += mtd->oobavail; ++ break; ++ case MTD_NANDECC_PLACE: ++ /* YAFFS1 legacy mode */ ++ oob_data += this->eccsteps * sizeof (int); ++ default: ++ oob_data += mtd->oobsize; ++ } ++ } ++ readdata: ++ /* Partial page read, transfer data into fs buffer */ ++ if (!aligned) { ++ for (j = col; j < end && read < len; j++) ++ buf[read++] = data_poi[j]; ++ this->pagebuf = realpage; ++ } else ++ read += mtd->oobblock; ++ ++ /* Apply delay or wait for ready/busy pin ++ * Do this before the AUTOINCR check, so no problems ++ * arise if a chip which does auto increment ++ * is marked as NOAUTOINCR by the board driver. ++ */ ++ if (!this->dev_ready) ++ udelay (this->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ ++ if (read == len) ++ break; ++ ++ /* For subsequent reads align to page boundary. */ ++ col = 0; ++ /* Increment page address */ ++ realpage++; ++ ++ page = realpage & this->pagemask; ++ /* Check, if we cross a chip boundary */ ++ if (!page) { ++ chipnr++; ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, chipnr); ++ } ++ /* Check, if the chip supports auto page increment ++ * or if we have hit a block boundary. ++ */ ++ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) ++ sndcmd = 1; ++ } ++ ++ /* Deselect and wake up anyone waiting on the device */ ++ if (flags & NAND_GET_DEVICE) ++ nand_release_device(mtd); ++ ++ /* ++ * Return success, if no ECC failures, else -EBADMSG ++ * fs driver will take care of that, because ++ * retlen == desired len and result == -EBADMSG ++ */ ++ *retlen = read; ++ return ecc_failed ? -EBADMSG : 0; ++} ++ ++/** ++ * nand_read_oob - [MTD Interface] NAND read out-of-band ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @retlen: pointer to variable to store the number of read bytes ++ * @buf: the databuffer to put data ++ * ++ * NAND read out-of-band data from the spare area ++ */ ++static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) ++{ ++ int i, col, page, chipnr; ++ struct nand_chip *this = mtd->priv; ++ int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; ++ ++ DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); ++ ++ /* Shift to get page */ ++ page = (int)(from >> this->page_shift); ++ chipnr = (int)(from >> this->chip_shift); ++ ++ /* Mask to get column */ ++ col = from & (mtd->oobsize - 1); ++ ++ /* Initialize return length value */ ++ *retlen = 0; ++ ++ /* Do not allow reads past end of device */ ++ if ((from + len) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); ++ *retlen = 0; ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd , FL_READING); ++ ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ ++ /* Send the read command */ ++ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); ++ /* ++ * Read the data, if we read more than one page ++ * oob data, let the device transfer the data ! ++ */ ++ i = 0; ++ while (i < len) { ++ int thislen = mtd->oobsize - col; ++ thislen = min_t(int, thislen, len); ++ this->read_buf(mtd, &buf[i], thislen); ++ i += thislen; ++ ++ /* Apply delay or wait for ready/busy pin ++ * Do this before the AUTOINCR check, so no problems ++ * arise if a chip which does auto increment ++ * is marked as NOAUTOINCR by the board driver. ++ */ ++ if (!this->dev_ready) ++ udelay (this->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ ++ /* Read more ? */ ++ if (i < len) { ++ page++; ++ col = 0; ++ ++ /* Check, if we cross a chip boundary */ ++ if (!(page & this->pagemask)) { ++ chipnr++; ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, chipnr); ++ } ++ ++ /* Check, if the chip supports auto page increment ++ * or if we have hit a block boundary. ++ */ ++ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { ++ /* For subsequent page reads set offset to 0 */ ++ this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); ++ } ++ } ++ } ++ ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ ++ /* Return happy */ ++ *retlen = len; ++ return 0; ++} ++ ++/** ++ * nand_read_raw - [GENERIC] Read raw data including oob into buffer ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @ooblen: number of oob data bytes to read ++ * ++ * Read raw data including oob into buffer ++ */ ++int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) ++{ ++ struct nand_chip *this = mtd->priv; ++ int page = (int) (from >> this->page_shift); ++ int chip = (int) (from >> this->chip_shift); ++ int sndcmd = 1; ++ int cnt = 0; ++ int pagesize = mtd->oobblock + mtd->oobsize; ++ int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; ++ ++ /* Do not allow reads past end of device */ ++ if ((from + len) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd , FL_READING); ++ ++ this->select_chip (mtd, chip); ++ ++ /* Add requested oob length */ ++ len += ooblen; ++ ++ while (len) { ++ if (sndcmd) ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); ++ sndcmd = 0; ++ ++ this->read_buf (mtd, &buf[cnt], pagesize); ++ ++ len -= pagesize; ++ cnt += pagesize; ++ page++; ++ ++ if (!this->dev_ready) ++ udelay (this->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ ++ /* Check, if the chip supports auto page increment */ ++ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) ++ sndcmd = 1; ++ } ++ ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ return 0; ++} ++ ++ ++/** ++ * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer ++ * @mtd: MTD device structure ++ * @fsbuf: buffer given by fs driver ++ * @oobsel: out of band selection structre ++ * @autoplace: 1 = place given buffer into the oob bytes ++ * @numpages: number of pages to prepare ++ * ++ * Return: ++ * 1. Filesystem buffer available and autoplacement is off, ++ * return filesystem buffer ++ * 2. No filesystem buffer or autoplace is off, return internal ++ * buffer ++ * 3. Filesystem buffer is given and autoplace selected ++ * put data from fs buffer into internal buffer and ++ * retrun internal buffer ++ * ++ * Note: The internal buffer is filled with 0xff. This must ++ * be done only once, when no autoplacement happens ++ * Autoplacement sets the buffer dirty flag, which ++ * forces the 0xff fill before using the buffer again. ++ * ++*/ ++static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, ++ int autoplace, int numpages) ++{ ++ struct nand_chip *this = mtd->priv; ++ int i, len, ofs; ++ ++ /* Zero copy fs supplied buffer */ ++ if (fsbuf && !autoplace) ++ return fsbuf; ++ ++ /* Check, if the buffer must be filled with ff again */ ++ if (this->oobdirty) { ++ memset (this->oob_buf, 0xff, ++ mtd->oobsize << (this->phys_erase_shift - this->page_shift)); ++ this->oobdirty = 0; ++ } ++ ++ /* If we have no autoplacement or no fs buffer use the internal one */ ++ if (!autoplace || !fsbuf) ++ return this->oob_buf; ++ ++ /* Walk through the pages and place the data */ ++ this->oobdirty = 1; ++ ofs = 0; ++ while (numpages--) { ++ for (i = 0, len = 0; len < mtd->oobavail; i++) { ++ int to = ofs + oobsel->oobfree[i][0]; ++ int num = oobsel->oobfree[i][1]; ++ memcpy (&this->oob_buf[to], fsbuf, num); ++ len += num; ++ fsbuf += num; ++ } ++ ofs += mtd->oobavail; ++ } ++ return this->oob_buf; ++} ++ ++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 ++ ++/** ++ * nand_write - [MTD Interface] compability function for nand_write_ecc ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @retlen: pointer to variable to store the number of written bytes ++ * @buf: the data to write ++ * ++ * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL ++ * ++*/ ++static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) ++{ ++ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); ++} ++ ++/** ++ * nand_write_ecc - [MTD Interface] NAND write with ECC ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @retlen: pointer to variable to store the number of written bytes ++ * @buf: the data to write ++ * @eccbuf: filesystem supplied oob data buffer ++ * @oobsel: oob selection structure ++ * ++ * NAND write with ECC ++ */ ++static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) ++{ ++ int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; ++ int autoplace = 0, numpages, totalpages; ++ struct nand_chip *this = mtd->priv; ++ u_char *oobbuf, *bufstart; ++ int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); ++ ++ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); ++ ++ /* Initialize retlen, in case of early exit */ ++ *retlen = 0; ++ ++ /* Do not allow write past end of device */ ++ if ((to + len) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); ++ return -EINVAL; ++ } ++ ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_WRITING); ++ ++ /* Calculate chipnr */ ++ chipnr = (int)(to >> this->chip_shift); ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) ++ goto out; ++ ++ /* if oobsel is NULL, use chip defaults */ ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ ++ /* Autoplace of oob data ? Use the default placement scheme */ ++ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { ++ oobsel = this->autooob; ++ autoplace = 1; ++ } ++ ++ /* Setup variables and oob buffer */ ++ totalpages = len >> this->page_shift; ++ page = (int) (to >> this->page_shift); ++ /* Invalidate the page cache, if we write to the cached page */ ++ if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) ++ this->pagebuf = -1; ++ ++ /* Set it relative to chip */ ++ page &= this->pagemask; ++ startpage = page; ++ /* Calc number of pages we can write in one go */ ++ numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); ++ oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); ++ bufstart = (u_char *)buf; ++ ++ /* Loop until all data is written */ ++ while (written < len) { ++ ++ this->data_poi = (u_char*) &buf[written]; ++ /* Write one page. If this is the last page to write ++ * or the last page in this block, then use the ++ * real pageprogram command, else select cached programming ++ * if supported by the chip. ++ */ ++ ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); ++ if (ret) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); ++ goto out; ++ } ++ /* Next oob page */ ++ oob += mtd->oobsize; ++ /* Update written bytes count */ ++ written += mtd->oobblock; ++ if (written == len) ++ goto cmp; ++ ++ /* Increment page address */ ++ page++; ++ ++ /* Have we hit a block boundary ? Then we have to verify and ++ * if verify is ok, we have to setup the oob buffer for ++ * the next pages. ++ */ ++ if (!(page & (ppblock - 1))){ ++ int ofs; ++ this->data_poi = bufstart; ++ ret = nand_verify_pages (mtd, this, startpage, ++ page - startpage, ++ oobbuf, oobsel, chipnr, (eccbuf != NULL)); ++ if (ret) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); ++ goto out; ++ } ++ *retlen = written; ++ ++ ofs = autoplace ? mtd->oobavail : mtd->oobsize; ++ if (eccbuf) ++ eccbuf += (page - startpage) * ofs; ++ totalpages -= page - startpage; ++ numpages = min (totalpages, ppblock); ++ page &= this->pagemask; ++ startpage = page; ++ oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, ++ autoplace, numpages); ++ /* Check, if we cross a chip boundary */ ++ if (!page) { ++ chipnr++; ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, chipnr); ++ } ++ } ++ } ++ /* Verify the remaining pages */ ++cmp: ++ this->data_poi = bufstart; ++ ret = nand_verify_pages (mtd, this, startpage, totalpages, ++ oobbuf, oobsel, chipnr, (eccbuf != NULL)); ++ if (!ret) ++ *retlen = written; ++ else ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); ++ ++out: ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ ++ return ret; ++} ++ ++ ++/** ++ * nand_write_oob - [MTD Interface] NAND write out-of-band ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @retlen: pointer to variable to store the number of written bytes ++ * @buf: the data to write ++ * ++ * NAND write out-of-band ++ */ ++static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) ++{ ++ int column, page, status, ret = -EIO, chipnr; ++ struct nand_chip *this = mtd->priv; ++ ++ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); ++ ++ /* Shift to get page */ ++ page = (int) (to >> this->page_shift); ++ chipnr = (int) (to >> this->chip_shift); ++ ++ /* Mask to get column */ ++ column = to & (mtd->oobsize - 1); ++ ++ /* Initialize return length value */ ++ *retlen = 0; ++ ++ /* Do not allow write past end of page */ ++ if ((column + len) > mtd->oobsize) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_WRITING); ++ ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ ++ /* Reset the chip. Some chips (like the Toshiba TC5832DC found ++ in one of my DiskOnChip 2000 test units) will clear the whole ++ data page too if we don't do this. I have no clue why, but ++ I seem to have 'fixed' it in the doc2000 driver in ++ August 1999. dwmw2. */ ++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) ++ goto out; ++ ++ /* Invalidate the page cache, if we write to the cached page */ ++ if (page == this->pagebuf) ++ this->pagebuf = -1; ++ ++ if (NAND_MUST_PAD(this)) { ++ /* Write out desired data */ ++ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); ++ /* prepad 0xff for partial programming */ ++ this->write_buf(mtd, ffchars, column); ++ /* write data */ ++ this->write_buf(mtd, buf, len); ++ /* postpad 0xff for partial programming */ ++ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); ++ } else { ++ /* Write out desired data */ ++ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); ++ /* write data */ ++ this->write_buf(mtd, buf, len); ++ } ++ /* Send command to program the OOB data */ ++ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = this->waitfunc (mtd, this, FL_WRITING); ++ ++ /* See if device thinks it succeeded */ ++ if (status & NAND_STATUS_FAIL) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ++ ret = -EIO; ++ goto out; ++ } ++ /* Return happy */ ++ *retlen = len; ++ ++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE ++ /* Send command to read back the data */ ++ this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); ++ ++ if (this->verify_buf(mtd, buf, len)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); ++ ret = -EIO; ++ goto out; ++ } ++#endif ++ ret = 0; ++out: ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ ++ return ret; ++} ++ ++ ++/** ++ * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc ++ * @mtd: MTD device structure ++ * @vecs: the iovectors to write ++ * @count: number of vectors ++ * @to: offset to write to ++ * @retlen: pointer to variable to store the number of written bytes ++ * ++ * NAND write with kvec. This just calls the ecc function ++ */ ++static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, ++ loff_t to, size_t * retlen) ++{ ++ return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); ++} ++ ++/** ++ * nand_writev_ecc - [MTD Interface] write with iovec with ecc ++ * @mtd: MTD device structure ++ * @vecs: the iovectors to write ++ * @count: number of vectors ++ * @to: offset to write to ++ * @retlen: pointer to variable to store the number of written bytes ++ * @eccbuf: filesystem supplied oob data buffer ++ * @oobsel: oob selection structure ++ * ++ * NAND write with iovec with ecc ++ */ ++static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, ++ loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) ++{ ++ int i, page, len, total_len, ret = -EIO, written = 0, chipnr; ++ int oob, numpages, autoplace = 0, startpage; ++ struct nand_chip *this = mtd->priv; ++ int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); ++ u_char *oobbuf, *bufstart; ++ ++ /* Preset written len for early exit */ ++ *retlen = 0; ++ ++ /* Calculate total length of data */ ++ total_len = 0; ++ for (i = 0; i < count; i++) ++ total_len += (int) vecs[i].iov_len; ++ ++ DEBUG (MTD_DEBUG_LEVEL3, ++ "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); ++ ++ /* Do not allow write past end of page */ ++ if ((to + total_len) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); ++ return -EINVAL; ++ } ++ ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(total_len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); ++ return -EINVAL; ++ } ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_WRITING); ++ ++ /* Get the current chip-nr */ ++ chipnr = (int) (to >> this->chip_shift); ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) ++ goto out; ++ ++ /* if oobsel is NULL, use chip defaults */ ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ ++ /* Autoplace of oob data ? Use the default placement scheme */ ++ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { ++ oobsel = this->autooob; ++ autoplace = 1; ++ } ++ ++ /* Setup start page */ ++ page = (int) (to >> this->page_shift); ++ /* Invalidate the page cache, if we write to the cached page */ ++ if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) ++ this->pagebuf = -1; ++ ++ startpage = page & this->pagemask; ++ ++ /* Loop until all kvec' data has been written */ ++ len = 0; ++ while (count) { ++ /* If the given tuple is >= pagesize then ++ * write it out from the iov ++ */ ++ if ((vecs->iov_len - len) >= mtd->oobblock) { ++ /* Calc number of pages we can write ++ * out of this iov in one go */ ++ numpages = (vecs->iov_len - len) >> this->page_shift; ++ /* Do not cross block boundaries */ ++ numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); ++ oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); ++ bufstart = (u_char *)vecs->iov_base; ++ bufstart += len; ++ this->data_poi = bufstart; ++ oob = 0; ++ for (i = 1; i <= numpages; i++) { ++ /* Write one page. If this is the last page to write ++ * then use the real pageprogram command, else select ++ * cached programming if supported by the chip. ++ */ ++ ret = nand_write_page (mtd, this, page & this->pagemask, ++ &oobbuf[oob], oobsel, i != numpages); ++ if (ret) ++ goto out; ++ this->data_poi += mtd->oobblock; ++ len += mtd->oobblock; ++ oob += mtd->oobsize; ++ page++; ++ } ++ /* Check, if we have to switch to the next tuple */ ++ if (len >= (int) vecs->iov_len) { ++ vecs++; ++ len = 0; ++ count--; ++ } ++ } else { ++ /* We must use the internal buffer, read data out of each ++ * tuple until we have a full page to write ++ */ ++ int cnt = 0; ++ while (cnt < mtd->oobblock) { ++ if (vecs->iov_base != NULL && vecs->iov_len) ++ this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; ++ /* Check, if we have to switch to the next tuple */ ++ if (len >= (int) vecs->iov_len) { ++ vecs++; ++ len = 0; ++ count--; ++ } ++ } ++ this->pagebuf = page; ++ this->data_poi = this->data_buf; ++ bufstart = this->data_poi; ++ numpages = 1; ++ oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); ++ ret = nand_write_page (mtd, this, page & this->pagemask, ++ oobbuf, oobsel, 0); ++ if (ret) ++ goto out; ++ page++; ++ } ++ ++ this->data_poi = bufstart; ++ ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); ++ if (ret) ++ goto out; ++ ++ written += mtd->oobblock * numpages; ++ /* All done ? */ ++ if (!count) ++ break; ++ ++ startpage = page & this->pagemask; ++ /* Check, if we cross a chip boundary */ ++ if (!startpage) { ++ chipnr++; ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, chipnr); ++ } ++ } ++ ret = 0; ++out: ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ ++ *retlen = written; ++ return ret; ++} ++ ++/** ++ * single_erease_cmd - [GENERIC] NAND standard block erase command function ++ * @mtd: MTD device structure ++ * @page: the page address of the block which will be erased ++ * ++ * Standard erase command for NAND chips ++ */ ++static void single_erase_cmd (struct mtd_info *mtd, int page) ++{ ++ struct nand_chip *this = mtd->priv; ++ /* Send commands to erase a block */ ++ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); ++ this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); ++} ++ ++/** ++ * multi_erease_cmd - [GENERIC] AND specific block erase command function ++ * @mtd: MTD device structure ++ * @page: the page address of the block which will be erased ++ * ++ * AND multi block erase command function ++ * Erase 4 consecutive blocks ++ */ ++static void multi_erase_cmd (struct mtd_info *mtd, int page) ++{ ++ struct nand_chip *this = mtd->priv; ++ /* Send commands to erase a block */ ++ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); ++ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); ++ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); ++ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); ++ this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); ++} ++ ++/** ++ * nand_erase - [MTD Interface] erase block(s) ++ * @mtd: MTD device structure ++ * @instr: erase instruction ++ * ++ * Erase one ore more blocks ++ */ ++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) ++{ ++ return nand_erase_nand (mtd, instr, 0); ++} ++ ++#define BBT_PAGE_MASK 0xffffff3f ++/** ++ * nand_erase_intern - [NAND Interface] erase block(s) ++ * @mtd: MTD device structure ++ * @instr: erase instruction ++ * @allowbbt: allow erasing the bbt area ++ * ++ * Erase one ore more blocks ++ */ ++int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) ++{ ++ int page, len, status, pages_per_block, ret, chipnr; ++ struct nand_chip *this = mtd->priv; ++ int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ ++ unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ ++ /* It is used to see if the current page is in the same */ ++ /* 256 block group and the same bank as the bbt. */ ++ ++ DEBUG (MTD_DEBUG_LEVEL3, ++ "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); ++ ++ /* Start address must align on block boundary */ ++ if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); ++ return -EINVAL; ++ } ++ ++ /* Length must align on block boundary */ ++ if (instr->len & ((1 << this->phys_erase_shift) - 1)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); ++ return -EINVAL; ++ } ++ ++ /* Do not allow erase past end of device */ ++ if ((instr->len + instr->addr) > mtd->size) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); ++ return -EINVAL; ++ } ++ ++ instr->fail_addr = 0xffffffff; ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_ERASING); ++ ++ /* Shift to get first page */ ++ page = (int) (instr->addr >> this->page_shift); ++ chipnr = (int) (instr->addr >> this->chip_shift); ++ ++ /* Calculate pages in each block */ ++ pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); ++ ++ /* Select the NAND device */ ++ this->select_chip(mtd, chipnr); ++ ++ /* Check the WP bit */ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ ++ if (this->options & BBT_AUTO_REFRESH) { ++ bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; ++ } else { ++ bbt_masked_page = 0xffffffff; /* should not match anything */ ++ } ++ ++ /* Loop through the pages */ ++ len = instr->len; ++ ++ instr->state = MTD_ERASING; ++ ++ while (len) { ++ /* Check if we have a bad block, we do not erase bad blocks ! */ ++ if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { ++ printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* Invalidate the page cache, if we erase the block which contains ++ the current cached page */ ++ if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) ++ this->pagebuf = -1; ++ ++ this->erase_cmd (mtd, page & this->pagemask); ++ ++ status = this->waitfunc (mtd, this, FL_ERASING); ++ ++ /* See if operation failed and additional status checks are available */ ++ if ((status & NAND_STATUS_FAIL) && (this->errstat)) { ++ status = this->errstat(mtd, this, FL_ERASING, status, page); ++ } ++ ++ /* See if block erase succeeded */ ++ if (status & NAND_STATUS_FAIL) { ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); ++ instr->state = MTD_ERASE_FAILED; ++ instr->fail_addr = (page << this->page_shift); ++ goto erase_exit; ++ } ++ ++ /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ ++ if (this->options & BBT_AUTO_REFRESH) { ++ if (((page & BBT_PAGE_MASK) == bbt_masked_page) && ++ (page != this->bbt_td->pages[chipnr])) { ++ rewrite_bbt[chipnr] = (page << this->page_shift); ++ } ++ } ++ ++ /* Increment page address and decrement length */ ++ len -= (1 << this->phys_erase_shift); ++ page += pages_per_block; ++ ++ /* Check, if we cross a chip boundary */ ++ if (len && !(page & this->pagemask)) { ++ chipnr++; ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, chipnr); ++ ++ /* if BBT requires refresh and BBT-PERCHIP, ++ * set the BBT page mask to see if this BBT should be rewritten */ ++ if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { ++ bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; ++ } ++ ++ } ++ } ++ instr->state = MTD_ERASE_DONE; ++ ++erase_exit: ++ ++ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; ++ /* Do call back function */ ++ if (!ret) ++ mtd_erase_callback(instr); ++ ++ /* Deselect and wake up anyone waiting on the device */ ++ nand_release_device(mtd); ++ ++ /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ ++ if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { ++ for (chipnr = 0; chipnr < this->numchips; chipnr++) { ++ if (rewrite_bbt[chipnr]) { ++ /* update the BBT for chip */ ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", ++ chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); ++ nand_update_bbt (mtd, rewrite_bbt[chipnr]); ++ } ++ } ++ } ++ ++ /* Return more or less happy */ ++ return ret; ++} ++ ++/** ++ * nand_sync - [MTD Interface] sync ++ * @mtd: MTD device structure ++ * ++ * Sync is actually a wait for chip ready function ++ */ ++static void nand_sync (struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device (this, mtd, FL_SYNCING); ++ /* Release it and go back */ ++ nand_release_device (mtd); ++} ++ ++ ++/** ++ * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad ++ * @mtd: MTD device structure ++ * @ofs: offset relative to mtd start ++ */ ++static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) ++{ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ ++ return nand_block_checkbad (mtd, ofs, 1, 0); ++} ++ ++/** ++ * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad ++ * @mtd: MTD device structure ++ * @ofs: offset relative to mtd start ++ */ ++static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *this = mtd->priv; ++ int ret; ++ ++ if ((ret = nand_block_isbad(mtd, ofs))) { ++ /* If it was bad already, return success and do nothing. */ ++ if (ret > 0) ++ return 0; ++ return ret; ++ } ++ ++ return this->block_markbad(mtd, ofs); ++} ++ ++/** ++ * nand_scan - [NAND Interface] Scan for the NAND device ++ * @mtd: MTD device structure ++ * @maxchips: Number of chips to scan for ++ * ++ * This fills out all the not initialized function pointers ++ * with the defaults. ++ * The flash ID is read and the mtd/chip structures are ++ * filled with the appropriate values. Buffers are allocated if ++ * they are not provided by the board driver ++ * ++ */ ++int nand_scan (struct mtd_info *mtd, int maxchips) ++{ ++ int i, j, nand_maf_id, nand_dev_id, busw, maf_id; ++ struct nand_chip *this = mtd->priv; ++ ++ /* Get buswidth to select the correct functions*/ ++ busw = this->options & NAND_BUSWIDTH_16; ++ ++ /* check for proper chip_delay setup, set 20us if not */ ++ if (!this->chip_delay) ++ this->chip_delay = 20; ++ ++ /* check, if a user supplied command function given */ ++ if (this->cmdfunc == NULL) ++ this->cmdfunc = nand_command; ++ ++ /* check, if a user supplied wait function given */ ++ if (this->waitfunc == NULL) ++ this->waitfunc = nand_wait; ++ ++ if (!this->select_chip) ++ this->select_chip = nand_select_chip; ++ if (!this->write_byte) ++ this->write_byte = busw ? nand_write_byte16 : nand_write_byte; ++ if (!this->read_byte) ++ this->read_byte = busw ? nand_read_byte16 : nand_read_byte; ++ if (!this->write_word) ++ this->write_word = nand_write_word; ++ if (!this->read_word) ++ this->read_word = nand_read_word; ++ if (!this->block_bad) ++ this->block_bad = nand_block_bad; ++ if (!this->block_markbad) ++ this->block_markbad = nand_default_block_markbad; ++ if (!this->write_buf) ++ this->write_buf = busw ? nand_write_buf16 : nand_write_buf; ++ if (!this->read_buf) ++ this->read_buf = busw ? nand_read_buf16 : nand_read_buf; ++ if (!this->verify_buf) ++ this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; ++ if (!this->scan_bbt) ++ this->scan_bbt = nand_default_bbt; ++ ++ /* Select the device */ ++ this->select_chip(mtd, 0); ++ ++ /* Send the command for reading device ID */ ++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); ++ ++ /* Read manufacturer and device IDs */ ++ nand_maf_id = this->read_byte(mtd); ++ nand_dev_id = this->read_byte(mtd); ++ ++ /* Print and store flash device information */ ++ for (i = 0; nand_flash_ids[i].name != NULL; i++) { ++ ++ if (nand_dev_id != nand_flash_ids[i].id) ++ continue; ++ ++ if (!mtd->name) mtd->name = nand_flash_ids[i].name; ++ this->chipsize = nand_flash_ids[i].chipsize << 20; ++ ++ /* New devices have all the information in additional id bytes */ ++ if (!nand_flash_ids[i].pagesize) { ++ int extid; ++ /* The 3rd id byte contains non relevant data ATM */ ++ extid = this->read_byte(mtd); ++ /* The 4th id byte is the important one */ ++ extid = this->read_byte(mtd); ++ /* Calc pagesize */ ++ mtd->oobblock = 1024 << (extid & 0x3); ++ extid >>= 2; ++ /* Calc oobsize */ ++ mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); ++ extid >>= 2; ++ /* Calc blocksize. Blocksize is multiples of 64KiB */ ++ mtd->erasesize = (64 * 1024) << (extid & 0x03); ++ extid >>= 2; ++ /* Get buswidth information */ ++ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; ++ ++ } else { ++ /* Old devices have this data hardcoded in the ++ * device id table */ ++ mtd->erasesize = nand_flash_ids[i].erasesize; ++ mtd->oobblock = nand_flash_ids[i].pagesize; ++ mtd->oobsize = mtd->oobblock / 32; ++ busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; ++ } ++ ++ /* Try to identify manufacturer */ ++ for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { ++ if (nand_manuf_ids[maf_id].id == nand_maf_id) ++ break; ++ } ++ ++ /* Check, if buswidth is correct. Hardware drivers should set ++ * this correct ! */ ++ if (busw != (this->options & NAND_BUSWIDTH_16)) { ++ printk (KERN_INFO "NAND device: Manufacturer ID:" ++ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, ++ nand_manuf_ids[maf_id].name , mtd->name); ++ printk (KERN_WARNING ++ "NAND bus width %d instead %d bit\n", ++ (this->options & NAND_BUSWIDTH_16) ? 16 : 8, ++ busw ? 16 : 8); ++ this->select_chip(mtd, -1); ++ return 1; ++ } ++ ++ /* Calculate the address shift from the page size */ ++ this->page_shift = ffs(mtd->oobblock) - 1; ++ this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; ++ this->chip_shift = ffs(this->chipsize) - 1; ++ ++ /* Set the bad block position */ ++ this->badblockpos = mtd->oobblock > 512 ? ++ NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; ++ ++ /* Get chip options, preserve non chip based options */ ++ this->options &= ~NAND_CHIPOPTIONS_MSK; ++ this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; ++ /* Set this as a default. Board drivers can override it, if neccecary */ ++ this->options |= NAND_NO_AUTOINCR; ++ /* Check if this is a not a samsung device. Do not clear the options ++ * for chips which are not having an extended id. ++ */ ++ if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) ++ this->options &= ~NAND_SAMSUNG_LP_OPTIONS; ++ ++ /* Check for AND chips with 4 page planes */ ++ if (this->options & NAND_4PAGE_ARRAY) ++ this->erase_cmd = multi_erase_cmd; ++ else ++ this->erase_cmd = single_erase_cmd; ++ ++ /* Do not replace user supplied command function ! */ ++ if (mtd->oobblock > 512 && this->cmdfunc == nand_command) ++ this->cmdfunc = nand_command_lp; ++ ++ printk (KERN_INFO "NAND device: Manufacturer ID:" ++ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, ++ nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); ++ break; ++ } ++ ++ if (!nand_flash_ids[i].name) { ++ printk (KERN_WARNING "No NAND device found!!!\n"); ++ this->select_chip(mtd, -1); ++ return 1; ++ } ++ ++ for (i=1; i < maxchips; i++) { ++ this->select_chip(mtd, i); ++ ++ /* Send the command for reading device ID */ ++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); ++ ++ /* Read manufacturer and device IDs */ ++ if (nand_maf_id != this->read_byte(mtd) || ++ nand_dev_id != this->read_byte(mtd)) ++ break; ++ } ++ if (i > 1) ++ printk(KERN_INFO "%d NAND chips detected\n", i); ++ ++ /* Allocate buffers, if neccecary */ ++ if (!this->oob_buf) { ++ size_t len; ++ len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); ++ this->oob_buf = kmalloc (len, GFP_KERNEL); ++ if (!this->oob_buf) { ++ printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); ++ return -ENOMEM; ++ } ++ this->options |= NAND_OOBBUF_ALLOC; ++ } ++ ++ if (!this->data_buf) { ++ size_t len; ++ len = mtd->oobblock + mtd->oobsize; ++ this->data_buf = kmalloc (len, GFP_KERNEL); ++ if (!this->data_buf) { ++ if (this->options & NAND_OOBBUF_ALLOC) ++ kfree (this->oob_buf); ++ printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); ++ return -ENOMEM; ++ } ++ this->options |= NAND_DATABUF_ALLOC; ++ } ++ ++ /* Store the number of chips and calc total size for mtd */ ++ this->numchips = i; ++ mtd->size = i * this->chipsize; ++ /* Convert chipsize to number of pages per chip -1. */ ++ this->pagemask = (this->chipsize >> this->page_shift) - 1; ++ /* Preset the internal oob buffer */ ++ memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); ++ ++ /* If no default placement scheme is given, select an ++ * appropriate one */ ++ if (!this->autooob) { ++ /* Select the appropriate default oob placement scheme for ++ * placement agnostic filesystems */ ++ switch (mtd->oobsize) { ++ case 8: ++ this->autooob = &nand_oob_8; ++ break; ++ case 16: ++ this->autooob = &nand_oob_16; ++ break; ++ case 64: ++ this->autooob = &nand_oob_64; ++ break; ++ default: ++ printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", ++ mtd->oobsize); ++ BUG(); ++ } ++ } ++ ++ /* The number of bytes available for the filesystem to place fs dependend ++ * oob data */ ++ if (this->options & NAND_BUSWIDTH_16) { ++ mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); ++ if (this->autooob->eccbytes & 0x01) ++ mtd->oobavail--; ++ } else ++ mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); ++ ++ /* ++ * check ECC mode, default to software ++ * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize ++ * fallback to software ECC ++ */ ++ this->eccsize = 256; /* set default eccsize */ ++ this->eccbytes = 3; ++ ++ switch (this->eccmode) { ++ case NAND_ECC_HW12_2048: ++ if (mtd->oobblock < 2048) { ++ printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", ++ mtd->oobblock); ++ this->eccmode = NAND_ECC_SOFT; ++ this->calculate_ecc = nand_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ } else ++ this->eccsize = 2048; ++ break; ++ ++ case NAND_ECC_HW3_512: ++ case NAND_ECC_HW6_512: ++ case NAND_ECC_HW8_512: ++ if (mtd->oobblock == 256) { ++ printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); ++ this->eccmode = NAND_ECC_SOFT; ++ this->calculate_ecc = nand_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ } else ++ this->eccsize = 512; /* set eccsize to 512 */ ++ break; ++ ++ case NAND_ECC_HW3_256: ++ break; ++ ++ case NAND_ECC_NONE: ++ printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); ++ this->eccmode = NAND_ECC_NONE; ++ break; ++ ++ case NAND_ECC_SOFT: ++ this->calculate_ecc = nand_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ break; ++ ++ default: ++ printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); ++ BUG(); ++ } ++ ++ /* Check hardware ecc function availability and adjust number of ecc bytes per ++ * calculation step ++ */ ++ switch (this->eccmode) { ++ case NAND_ECC_HW12_2048: ++ this->eccbytes += 4; ++ case NAND_ECC_HW8_512: ++ this->eccbytes += 2; ++ case NAND_ECC_HW6_512: ++ this->eccbytes += 3; ++ case NAND_ECC_HW3_512: ++ case NAND_ECC_HW3_256: ++ if (this->calculate_ecc && this->correct_data && this->enable_hwecc) ++ break; ++ printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); ++ BUG(); ++ } ++ ++ mtd->eccsize = this->eccsize; ++ ++ /* Set the number of read / write steps for one page to ensure ECC generation */ ++ switch (this->eccmode) { ++ case NAND_ECC_HW12_2048: ++ this->eccsteps = mtd->oobblock / 2048; ++ break; ++ case NAND_ECC_HW3_512: ++ case NAND_ECC_HW6_512: ++ case NAND_ECC_HW8_512: ++ this->eccsteps = mtd->oobblock / 512; ++ break; ++ case NAND_ECC_HW3_256: ++ case NAND_ECC_SOFT: ++ this->eccsteps = mtd->oobblock / 256; ++ break; ++ ++ case NAND_ECC_NONE: ++ this->eccsteps = 1; ++ break; ++ } ++ ++ /* Initialize state, waitqueue and spinlock */ ++ this->state = FL_READY; ++ init_waitqueue_head (&this->wq); ++ spin_lock_init (&this->chip_lock); ++ ++ /* De-select the device */ ++ this->select_chip(mtd, -1); ++ ++ /* Invalidate the pagebuffer reference */ ++ this->pagebuf = -1; ++ ++ /* Fill in remaining MTD driver data */ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; ++ mtd->ecctype = MTD_ECC_SW; ++ mtd->erase = nand_erase; ++ mtd->point = NULL; ++ mtd->unpoint = NULL; ++ mtd->read = nand_read; ++ mtd->write = nand_write; ++ mtd->read_ecc = nand_read_ecc; ++ mtd->write_ecc = nand_write_ecc; ++ mtd->read_oob = nand_read_oob; ++ mtd->write_oob = nand_write_oob; ++ mtd->readv = NULL; ++ mtd->writev = nand_writev; ++ mtd->writev_ecc = nand_writev_ecc; ++ mtd->sync = nand_sync; ++ mtd->lock = NULL; ++ mtd->unlock = NULL; ++ mtd->suspend = NULL; ++ mtd->resume = NULL; ++ mtd->block_isbad = nand_block_isbad; ++ mtd->block_markbad = nand_block_markbad; ++ ++ /* and make the autooob the default one */ ++ memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); ++ ++ mtd->owner = THIS_MODULE; ++ ++ /* Check, if we should skip the bad block table scan */ ++ if (this->options & NAND_SKIP_BBTSCAN) ++ return 0; ++ ++ /* Build bad block table */ ++ return this->scan_bbt (mtd); ++} ++ ++/** ++ * nand_release - [NAND Interface] Free resources held by the NAND device ++ * @mtd: MTD device structure ++*/ ++void nand_release (struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ /* Deregister partitions */ ++ del_mtd_partitions (mtd); ++#endif ++ /* Deregister the device */ ++ del_mtd_device (mtd); ++ ++ /* Free bad block table memory, if allocated */ ++ if (this->bbt) ++ kfree (this->bbt); ++ /* Buffer allocated by nand_scan ? */ ++ if (this->options & NAND_OOBBUF_ALLOC) ++ kfree (this->oob_buf); ++ /* Buffer allocated by nand_scan ? */ ++ if (this->options & NAND_DATABUF_ALLOC) ++ kfree (this->data_buf); ++} ++ ++EXPORT_SYMBOL (nand_scan); ++EXPORT_SYMBOL (nand_release); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); ++MODULE_DESCRIPTION ("Generic NAND flash driver code"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/nand_bbt.c +@@ -0,0 +1,1081 @@ ++/* ++ * drivers/mtd/nand_bbt.c ++ * ++ * Overview: ++ * Bad block table support for the NAND driver ++ * ++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * $Id$ ++ * ++ * 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. ++ * ++ * Description: ++ * ++ * When nand_scan_bbt is called, then it tries to find the bad block table ++ * depending on the options in the bbt descriptor(s). If a bbt is found ++ * then the contents are read and the memory based bbt is created. If a ++ * mirrored bbt is selected then the mirror is searched too and the ++ * versions are compared. If the mirror has a greater version number ++ * than the mirror bbt is used to build the memory based bbt. ++ * If the tables are not versioned, then we "or" the bad block information. ++ * If one of the bbt's is out of date or does not exist it is (re)created. ++ * If no bbt exists at all then the device is scanned for factory marked ++ * good / bad blocks and the bad block tables are created. ++ * ++ * For manufacturer created bbts like the one found on M-SYS DOC devices ++ * the bbt is searched and read but never created ++ * ++ * The autogenerated bad block table is located in the last good blocks ++ * of the device. The table is mirrored, so it can be updated eventually. ++ * The table is marked in the oob area with an ident pattern and a version ++ * number which indicates which of both tables is more up to date. ++ * ++ * The table uses 2 bits per block ++ * 11b: block is good ++ * 00b: block is factory marked bad ++ * 01b, 10b: block is marked bad due to wear ++ * ++ * The memory bad block table uses the following scheme: ++ * 00b: block is good ++ * 01b: block is marked bad due to wear ++ * 10b: block is reserved (to protect the bbt area) ++ * 11b: block is factory marked bad ++ * ++ * Multichip devices like DOC store the bad block info per floor. ++ * ++ * Following assumptions are made: ++ * - bbts start at a page boundary, if autolocated on a block boundary ++ * - the space neccecary for a bbt in FLASH does not exceed a block boundary ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/** ++ * check_pattern - [GENERIC] check if a pattern is in the buffer ++ * @buf: the buffer to search ++ * @len: the length of buffer to search ++ * @paglen: the pagelength ++ * @td: search pattern descriptor ++ * ++ * Check for a pattern at the given place. Used to search bad block ++ * tables and good / bad block identifiers. ++ * If the SCAN_EMPTY option is set then check, if all bytes except the ++ * pattern area contain 0xff ++ * ++*/ ++static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) ++{ ++ int i, end = 0; ++ uint8_t *p = buf; ++ ++ if (td->options & NAND_BBT_SCANEMPTY) { ++ end = paglen + td->offs; ++ for (i = 0; i < end; i++) { ++ if (p[i] != 0xff) ++ return -1; ++ } ++ p += end; ++ } ++ ++ /* Compare the pattern */ ++ for (i = 0; i < td->len; i++) { ++ if (p[i] != td->pattern[i]) ++ return -1; ++ } ++ ++ if (td->options & NAND_BBT_SCANEMPTY) { ++ p += td->len; ++ end += td->len; ++ for (i = end; i < len; i++) { ++ if (*p++ != 0xff) ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * read_bbt - [GENERIC] Read the bad block table starting from page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @page: the starting page ++ * @num: the number of bbt descriptors to read ++ * @bits: number of bits per block ++ * @offs: offset in the memory table ++ * @reserved_block_code: Pattern to identify reserved blocks ++ * ++ * Read the bad block table starting from page. ++ * ++ */ ++static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, ++ int bits, int offs, int reserved_block_code) ++{ ++ int res, i, j, act = 0; ++ struct nand_chip *this = mtd->priv; ++ size_t retlen, len, totlen; ++ loff_t from; ++ uint8_t msk = (uint8_t) ((1 << bits) - 1); ++ ++ totlen = (num * bits) >> 3; ++ from = ((loff_t)page) << this->page_shift; ++ ++ while (totlen) { ++ len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); ++ res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); ++ if (res < 0) { ++ if (retlen != len) { ++ printk (KERN_INFO "nand_bbt: Error reading bad block table\n"); ++ return res; ++ } ++ printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); ++ } ++ ++ /* Analyse data */ ++ for (i = 0; i < len; i++) { ++ uint8_t dat = buf[i]; ++ for (j = 0; j < 8; j += bits, act += 2) { ++ uint8_t tmp = (dat >> j) & msk; ++ if (tmp == msk) ++ continue; ++ if (reserved_block_code && ++ (tmp == reserved_block_code)) { ++ printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", ++ ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); ++ this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); ++ continue; ++ } ++ /* Leave it for now, if its matured we can move this ++ * message to MTD_DEBUG_LEVEL0 */ ++ printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", ++ ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); ++ /* Factory marked bad or worn out ? */ ++ if (tmp == 0) ++ this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); ++ else ++ this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); ++ } ++ } ++ totlen -= len; ++ from += len; ++ } ++ return 0; ++} ++ ++/** ++ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @chip: read the table for a specific chip, -1 read all chips. ++ * Applies only if NAND_BBT_PERCHIP option is set ++ * ++ * Read the bad block table for all chips starting at a given page ++ * We assume that the bbt bits are in consecutive order. ++*/ ++static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ int res = 0, i; ++ int bits; ++ ++ bits = td->options & NAND_BBT_NRBITS_MSK; ++ if (td->options & NAND_BBT_PERCHIP) { ++ int offs = 0; ++ for (i = 0; i < this->numchips; i++) { ++ if (chip == -1 || chip == i) ++ res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); ++ if (res) ++ return res; ++ offs += this->chipsize >> (this->bbt_erase_shift + 2); ++ } ++ } else { ++ res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); ++ if (res) ++ return res; ++ } ++ return 0; ++} ++ ++/** ++ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * ++ * Read the bad block table(s) for all chips starting at a given page ++ * We assume that the bbt bits are in consecutive order. ++ * ++*/ ++static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, ++ struct nand_bbt_descr *md) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ /* Read the primary version, if available */ ++ if (td->options & NAND_BBT_VERSION) { ++ nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); ++ td->version[0] = buf[mtd->oobblock + td->veroffs]; ++ printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); ++ } ++ ++ /* Read the mirror version, if available */ ++ if (md && (md->options & NAND_BBT_VERSION)) { ++ nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); ++ md->version[0] = buf[mtd->oobblock + md->veroffs]; ++ printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); ++ } ++ ++ return 1; ++} ++ ++/** ++ * create_bbt - [GENERIC] Create a bad block table by scanning the device ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @bd: descriptor for the good/bad block search pattern ++ * @chip: create the table for a specific chip, -1 read all chips. ++ * Applies only if NAND_BBT_PERCHIP option is set ++ * ++ * Create a bad block table by scanning the device ++ * for the given good/bad block identify pattern ++ */ ++static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ int i, j, numblocks, len, scanlen; ++ int startblock; ++ loff_t from; ++ size_t readlen, ooblen; ++ ++ printk (KERN_INFO "Scanning device for bad blocks\n"); ++ ++ if (bd->options & NAND_BBT_SCANALLPAGES) ++ len = 1 << (this->bbt_erase_shift - this->page_shift); ++ else { ++ if (bd->options & NAND_BBT_SCAN2NDPAGE) ++ len = 2; ++ else ++ len = 1; ++ } ++ ++ if (!(bd->options & NAND_BBT_SCANEMPTY)) { ++ /* We need only read few bytes from the OOB area */ ++ scanlen = ooblen = 0; ++ readlen = bd->len; ++ } else { ++ /* Full page content should be read */ ++ scanlen = mtd->oobblock + mtd->oobsize; ++ readlen = len * mtd->oobblock; ++ ooblen = len * mtd->oobsize; ++ } ++ ++ if (chip == -1) { ++ /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it ++ * makes shifting and masking less painful */ ++ numblocks = mtd->size >> (this->bbt_erase_shift - 1); ++ startblock = 0; ++ from = 0; ++ } else { ++ if (chip >= this->numchips) { ++ printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", ++ chip + 1, this->numchips); ++ return -EINVAL; ++ } ++ numblocks = this->chipsize >> (this->bbt_erase_shift - 1); ++ startblock = chip * numblocks; ++ numblocks += startblock; ++ from = startblock << (this->bbt_erase_shift - 1); ++ } ++ ++ for (i = startblock; i < numblocks;) { ++ int ret; ++ ++ if (bd->options & NAND_BBT_SCANEMPTY) ++ if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) ++ return ret; ++ ++ for (j = 0; j < len; j++) { ++ if (!(bd->options & NAND_BBT_SCANEMPTY)) { ++ size_t retlen; ++ ++ /* No need to read pages fully, just read required OOB bytes */ ++ ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, ++ readlen, &retlen, &buf[0]); ++ if (ret) ++ return ret; ++ } ++ if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { ++ this->bbt[i >> 3] |= 0x03 << (i & 0x6); ++ printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", ++ i >> 1, (unsigned int) from); ++ break; ++ } ++ } ++ i += 2; ++ from += (1 << this->bbt_erase_shift); ++ } ++ ++ return 0; ++} ++ ++/** ++ * search_bbt - [GENERIC] scan the device for a specific bad block table ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * ++ * Read the bad block table by searching for a given ident pattern. ++ * Search is preformed either from the beginning up or from the end of ++ * the device downwards. The search starts always at the start of a ++ * block. ++ * If the option NAND_BBT_PERCHIP is given, each chip is searched ++ * for a bbt, which contains the bad block information of this chip. ++ * This is neccecary to provide support for certain DOC devices. ++ * ++ * The bbt ident pattern resides in the oob area of the first page ++ * in a block. ++ */ ++static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) ++{ ++ struct nand_chip *this = mtd->priv; ++ int i, chips; ++ int bits, startblock, block, dir; ++ int scanlen = mtd->oobblock + mtd->oobsize; ++ int bbtblocks; ++ ++ /* Search direction top -> down ? */ ++ if (td->options & NAND_BBT_LASTBLOCK) { ++ startblock = (mtd->size >> this->bbt_erase_shift) -1; ++ dir = -1; ++ } else { ++ startblock = 0; ++ dir = 1; ++ } ++ ++ /* Do we have a bbt per chip ? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chips = this->numchips; ++ bbtblocks = this->chipsize >> this->bbt_erase_shift; ++ startblock &= bbtblocks - 1; ++ } else { ++ chips = 1; ++ bbtblocks = mtd->size >> this->bbt_erase_shift; ++ } ++ ++ /* Number of bits for each erase block in the bbt */ ++ bits = td->options & NAND_BBT_NRBITS_MSK; ++ ++ for (i = 0; i < chips; i++) { ++ /* Reset version information */ ++ td->version[i] = 0; ++ td->pages[i] = -1; ++ /* Scan the maximum number of blocks */ ++ for (block = 0; block < td->maxblocks; block++) { ++ int actblock = startblock + dir * block; ++ /* Read first page */ ++ nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); ++ if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { ++ td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); ++ if (td->options & NAND_BBT_VERSION) { ++ td->version[i] = buf[mtd->oobblock + td->veroffs]; ++ } ++ break; ++ } ++ } ++ startblock += this->chipsize >> this->bbt_erase_shift; ++ } ++ /* Check, if we found a bbt for each requested chip */ ++ for (i = 0; i < chips; i++) { ++ if (td->pages[i] == -1) ++ printk (KERN_WARNING "Bad block table not found for chip %d\n", i); ++ else ++ printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); ++ } ++ return 0; ++} ++ ++/** ++ * search_read_bbts - [GENERIC] scan the device for bad block table(s) ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * ++ * Search and read the bad block table(s) ++*/ ++static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *td, struct nand_bbt_descr *md) ++{ ++ /* Search the primary table */ ++ search_bbt (mtd, buf, td); ++ ++ /* Search the mirror table */ ++ if (md) ++ search_bbt (mtd, buf, md); ++ ++ /* Force result check */ ++ return 1; ++} ++ ++ ++/** ++ * write_bbt - [GENERIC] (Re)write the bad block table ++ * ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * @chipsel: selector for a specific chip, -1 for all ++ * ++ * (Re)write the bad block table ++ * ++*/ ++static int write_bbt (struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct nand_oobinfo oobinfo; ++ struct erase_info einfo; ++ int i, j, res, chip = 0; ++ int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; ++ int nrchips, bbtoffs, pageoffs; ++ uint8_t msk[4]; ++ uint8_t rcode = td->reserved_block_code; ++ size_t retlen, len = 0; ++ loff_t to; ++ ++ if (!rcode) ++ rcode = 0xff; ++ /* Write bad block table per chip rather than per device ? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ numblocks = (int) (this->chipsize >> this->bbt_erase_shift); ++ /* Full device write or specific chip ? */ ++ if (chipsel == -1) { ++ nrchips = this->numchips; ++ } else { ++ nrchips = chipsel + 1; ++ chip = chipsel; ++ } ++ } else { ++ numblocks = (int) (mtd->size >> this->bbt_erase_shift); ++ nrchips = 1; ++ } ++ ++ /* Loop through the chips */ ++ for (; chip < nrchips; chip++) { ++ ++ /* There was already a version of the table, reuse the page ++ * This applies for absolute placement too, as we have the ++ * page nr. in td->pages. ++ */ ++ if (td->pages[chip] != -1) { ++ page = td->pages[chip]; ++ goto write; ++ } ++ ++ /* Automatic placement of the bad block table */ ++ /* Search direction top -> down ? */ ++ if (td->options & NAND_BBT_LASTBLOCK) { ++ startblock = numblocks * (chip + 1) - 1; ++ dir = -1; ++ } else { ++ startblock = chip * numblocks; ++ dir = 1; ++ } ++ ++ for (i = 0; i < td->maxblocks; i++) { ++ int block = startblock + dir * i; ++ /* Check, if the block is bad */ ++ switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { ++ case 0x01: ++ case 0x03: ++ continue; ++ } ++ page = block << (this->bbt_erase_shift - this->page_shift); ++ /* Check, if the block is used by the mirror table */ ++ if (!md || md->pages[chip] != page) ++ goto write; ++ } ++ printk (KERN_ERR "No space left to write bad block table\n"); ++ return -ENOSPC; ++write: ++ ++ /* Set up shift count and masks for the flash table */ ++ bits = td->options & NAND_BBT_NRBITS_MSK; ++ switch (bits) { ++ case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; ++ case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; ++ case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; ++ case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; ++ default: return -EINVAL; ++ } ++ ++ bbtoffs = chip * (numblocks >> 2); ++ ++ to = ((loff_t) page) << this->page_shift; ++ ++ memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); ++ oobinfo.useecc = MTD_NANDECC_PLACEONLY; ++ ++ /* Must we save the block contents ? */ ++ if (td->options & NAND_BBT_SAVECONTENT) { ++ /* Make it block aligned */ ++ to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); ++ len = 1 << this->bbt_erase_shift; ++ res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); ++ if (res < 0) { ++ if (retlen != len) { ++ printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); ++ return res; ++ } ++ printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); ++ } ++ /* Calc the byte offset in the buffer */ ++ pageoffs = page - (int)(to >> this->page_shift); ++ offs = pageoffs << this->page_shift; ++ /* Preset the bbt area with 0xff */ ++ memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); ++ /* Preset the bbt's oob area with 0xff */ ++ memset (&buf[len + pageoffs * mtd->oobsize], 0xff, ++ ((len >> this->page_shift) - pageoffs) * mtd->oobsize); ++ if (td->options & NAND_BBT_VERSION) { ++ buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; ++ } ++ } else { ++ /* Calc length */ ++ len = (size_t) (numblocks >> sft); ++ /* Make it page aligned ! */ ++ len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); ++ /* Preset the buffer with 0xff */ ++ memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); ++ offs = 0; ++ /* Pattern is located in oob area of first page */ ++ memcpy (&buf[len + td->offs], td->pattern, td->len); ++ if (td->options & NAND_BBT_VERSION) { ++ buf[len + td->veroffs] = td->version[chip]; ++ } ++ } ++ ++ /* walk through the memory table */ ++ for (i = 0; i < numblocks; ) { ++ uint8_t dat; ++ dat = this->bbt[bbtoffs + (i >> 2)]; ++ for (j = 0; j < 4; j++ , i++) { ++ int sftcnt = (i << (3 - sft)) & sftmsk; ++ /* Do not store the reserved bbt blocks ! */ ++ buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); ++ dat >>= 2; ++ } ++ } ++ ++ memset (&einfo, 0, sizeof (einfo)); ++ einfo.mtd = mtd; ++ einfo.addr = (unsigned long) to; ++ einfo.len = 1 << this->bbt_erase_shift; ++ res = nand_erase_nand (mtd, &einfo, 1); ++ if (res < 0) { ++ printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); ++ return res; ++ } ++ ++ res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); ++ if (res < 0) { ++ printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); ++ return res; ++ } ++ printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", ++ (unsigned int) to, td->version[chip]); ++ ++ /* Mark it as used */ ++ td->pages[chip] = page; ++ } ++ return 0; ++} ++ ++/** ++ * nand_memory_bbt - [GENERIC] create a memory based bad block table ++ * @mtd: MTD device structure ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function creates a memory based bbt by scanning the device ++ * for manufacturer / software marked good / bad blocks ++*/ ++static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ bd->options &= ~NAND_BBT_SCANEMPTY; ++ return create_bbt (mtd, this->data_buf, bd, -1); ++} ++ ++/** ++ * check_create - [GENERIC] create and write bbt(s) if neccecary ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function checks the results of the previous call to read_bbt ++ * and creates / updates the bbt(s) if neccecary ++ * Creation is neccecary if no bbt was found for the chip/device ++ * Update is neccecary if one of the tables is missing or the ++ * version nr. of one table is less than the other ++*/ ++static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) ++{ ++ int i, chips, writeops, chipsel, res; ++ struct nand_chip *this = mtd->priv; ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ struct nand_bbt_descr *rd, *rd2; ++ ++ /* Do we have a bbt per chip ? */ ++ if (td->options & NAND_BBT_PERCHIP) ++ chips = this->numchips; ++ else ++ chips = 1; ++ ++ for (i = 0; i < chips; i++) { ++ writeops = 0; ++ rd = NULL; ++ rd2 = NULL; ++ /* Per chip or per device ? */ ++ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; ++ /* Mirrored table avilable ? */ ++ if (md) { ++ if (td->pages[i] == -1 && md->pages[i] == -1) { ++ writeops = 0x03; ++ goto create; ++ } ++ ++ if (td->pages[i] == -1) { ++ rd = md; ++ td->version[i] = md->version[i]; ++ writeops = 1; ++ goto writecheck; ++ } ++ ++ if (md->pages[i] == -1) { ++ rd = td; ++ md->version[i] = td->version[i]; ++ writeops = 2; ++ goto writecheck; ++ } ++ ++ if (td->version[i] == md->version[i]) { ++ rd = td; ++ if (!(td->options & NAND_BBT_VERSION)) ++ rd2 = md; ++ goto writecheck; ++ } ++ ++ if (((int8_t) (td->version[i] - md->version[i])) > 0) { ++ rd = td; ++ md->version[i] = td->version[i]; ++ writeops = 2; ++ } else { ++ rd = md; ++ td->version[i] = md->version[i]; ++ writeops = 1; ++ } ++ ++ goto writecheck; ++ ++ } else { ++ if (td->pages[i] == -1) { ++ writeops = 0x01; ++ goto create; ++ } ++ rd = td; ++ goto writecheck; ++ } ++create: ++ /* Create the bad block table by scanning the device ? */ ++ if (!(td->options & NAND_BBT_CREATE)) ++ continue; ++ ++ /* Create the table in memory by scanning the chip(s) */ ++ create_bbt (mtd, buf, bd, chipsel); ++ ++ td->version[i] = 1; ++ if (md) ++ md->version[i] = 1; ++writecheck: ++ /* read back first ? */ ++ if (rd) ++ read_abs_bbt (mtd, buf, rd, chipsel); ++ /* If they weren't versioned, read both. */ ++ if (rd2) ++ read_abs_bbt (mtd, buf, rd2, chipsel); ++ ++ /* Write the bad block table to the device ? */ ++ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { ++ res = write_bbt (mtd, buf, td, md, chipsel); ++ if (res < 0) ++ return res; ++ } ++ ++ /* Write the mirror bad block table to the device ? */ ++ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { ++ res = write_bbt (mtd, buf, md, td, chipsel); ++ if (res < 0) ++ return res; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * mark_bbt_regions - [GENERIC] mark the bad block table regions ++ * @mtd: MTD device structure ++ * @td: bad block table descriptor ++ * ++ * The bad block table regions are marked as "bad" to prevent ++ * accidental erasures / writes. The regions are identified by ++ * the mark 0x02. ++*/ ++static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) ++{ ++ struct nand_chip *this = mtd->priv; ++ int i, j, chips, block, nrblocks, update; ++ uint8_t oldval, newval; ++ ++ /* Do we have a bbt per chip ? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chips = this->numchips; ++ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); ++ } else { ++ chips = 1; ++ nrblocks = (int)(mtd->size >> this->bbt_erase_shift); ++ } ++ ++ for (i = 0; i < chips; i++) { ++ if ((td->options & NAND_BBT_ABSPAGE) || ++ !(td->options & NAND_BBT_WRITE)) { ++ if (td->pages[i] == -1) continue; ++ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); ++ block <<= 1; ++ oldval = this->bbt[(block >> 3)]; ++ newval = oldval | (0x2 << (block & 0x06)); ++ this->bbt[(block >> 3)] = newval; ++ if ((oldval != newval) && td->reserved_block_code) ++ nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); ++ continue; ++ } ++ update = 0; ++ if (td->options & NAND_BBT_LASTBLOCK) ++ block = ((i + 1) * nrblocks) - td->maxblocks; ++ else ++ block = i * nrblocks; ++ block <<= 1; ++ for (j = 0; j < td->maxblocks; j++) { ++ oldval = this->bbt[(block >> 3)]; ++ newval = oldval | (0x2 << (block & 0x06)); ++ this->bbt[(block >> 3)] = newval; ++ if (oldval != newval) update = 1; ++ block += 2; ++ } ++ /* If we want reserved blocks to be recorded to flash, and some ++ new ones have been marked, then we need to update the stored ++ bbts. This should only happen once. */ ++ if (update && td->reserved_block_code) ++ nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); ++ } ++} ++ ++/** ++ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) ++ * @mtd: MTD device structure ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function checks, if a bad block table(s) is/are already ++ * available. If not it scans the device for manufacturer ++ * marked good / bad blocks and writes the bad block table(s) to ++ * the selected place. ++ * ++ * The bad block table memory is allocated here. It must be freed ++ * by calling the nand_free_bbt function. ++ * ++*/ ++int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) ++{ ++ struct nand_chip *this = mtd->priv; ++ int len, res = 0; ++ uint8_t *buf; ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ ++ len = mtd->size >> (this->bbt_erase_shift + 2); ++ /* Allocate memory (2bit per block) */ ++ this->bbt = kmalloc (len, GFP_KERNEL); ++ if (!this->bbt) { ++ printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); ++ return -ENOMEM; ++ } ++ /* Clear the memory bad block table */ ++ memset (this->bbt, 0x00, len); ++ ++ /* If no primary table decriptor is given, scan the device ++ * to build a memory based bad block table ++ */ ++ if (!td) { ++ if ((res = nand_memory_bbt(mtd, bd))) { ++ printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); ++ kfree (this->bbt); ++ this->bbt = NULL; ++ } ++ return res; ++ } ++ ++ /* Allocate a temporary buffer for one eraseblock incl. oob */ ++ len = (1 << this->bbt_erase_shift); ++ len += (len >> this->page_shift) * mtd->oobsize; ++ buf = kmalloc (len, GFP_KERNEL); ++ if (!buf) { ++ printk (KERN_ERR "nand_bbt: Out of memory\n"); ++ kfree (this->bbt); ++ this->bbt = NULL; ++ return -ENOMEM; ++ } ++ ++ /* Is the bbt at a given page ? */ ++ if (td->options & NAND_BBT_ABSPAGE) { ++ res = read_abs_bbts (mtd, buf, td, md); ++ } else { ++ /* Search the bad block table using a pattern in oob */ ++ res = search_read_bbts (mtd, buf, td, md); ++ } ++ ++ if (res) ++ res = check_create (mtd, buf, bd); ++ ++ /* Prevent the bbt regions from erasing / writing */ ++ mark_bbt_region (mtd, td); ++ if (md) ++ mark_bbt_region (mtd, md); ++ ++ kfree (buf); ++ return res; ++} ++ ++ ++/** ++ * nand_update_bbt - [NAND Interface] update bad block table(s) ++ * @mtd: MTD device structure ++ * @offs: the offset of the newly marked block ++ * ++ * The function updates the bad block table(s) ++*/ ++int nand_update_bbt (struct mtd_info *mtd, loff_t offs) ++{ ++ struct nand_chip *this = mtd->priv; ++ int len, res = 0, writeops = 0; ++ int chip, chipsel; ++ uint8_t *buf; ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ ++ if (!this->bbt || !td) ++ return -EINVAL; ++ ++ len = mtd->size >> (this->bbt_erase_shift + 2); ++ /* Allocate a temporary buffer for one eraseblock incl. oob */ ++ len = (1 << this->bbt_erase_shift); ++ len += (len >> this->page_shift) * mtd->oobsize; ++ buf = kmalloc (len, GFP_KERNEL); ++ if (!buf) { ++ printk (KERN_ERR "nand_update_bbt: Out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ writeops = md != NULL ? 0x03 : 0x01; ++ ++ /* Do we have a bbt per chip ? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chip = (int) (offs >> this->chip_shift); ++ chipsel = chip; ++ } else { ++ chip = 0; ++ chipsel = -1; ++ } ++ ++ td->version[chip]++; ++ if (md) ++ md->version[chip]++; ++ ++ /* Write the bad block table to the device ? */ ++ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { ++ res = write_bbt (mtd, buf, td, md, chipsel); ++ if (res < 0) ++ goto out; ++ } ++ /* Write the mirror bad block table to the device ? */ ++ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { ++ res = write_bbt (mtd, buf, md, td, chipsel); ++ } ++ ++out: ++ kfree (buf); ++ return res; ++} ++ ++/* Define some generic bad / good block scan pattern which are used ++ * while scanning a device for factory marked good / bad blocks. */ ++static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; ++ ++static struct nand_bbt_descr smallpage_memorybased = { ++ .options = NAND_BBT_SCAN2NDPAGE, ++ .offs = 5, ++ .len = 1, ++ .pattern = scan_ff_pattern ++}; ++ ++static struct nand_bbt_descr largepage_memorybased = { ++ .options = 0, ++ .offs = 0, ++ .len = 2, ++ .pattern = scan_ff_pattern ++}; ++ ++static struct nand_bbt_descr smallpage_flashbased = { ++ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, ++ .offs = 5, ++ .len = 1, ++ .pattern = scan_ff_pattern ++}; ++ ++static struct nand_bbt_descr largepage_flashbased = { ++ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, ++ .offs = 0, ++ .len = 2, ++ .pattern = scan_ff_pattern ++}; ++ ++static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; ++ ++static struct nand_bbt_descr agand_flashbased = { ++ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, ++ .offs = 0x20, ++ .len = 6, ++ .pattern = scan_agand_pattern ++}; ++ ++/* Generic flash bbt decriptors ++*/ ++static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; ++static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 8, ++ .len = 4, ++ .veroffs = 12, ++ .maxblocks = 4, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 8, ++ .len = 4, ++ .veroffs = 12, ++ .maxblocks = 4, ++ .pattern = mirror_pattern ++}; ++ ++/** ++ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device ++ * @mtd: MTD device structure ++ * ++ * This function selects the default bad block table ++ * support for the device and calls the nand_scan_bbt function ++ * ++*/ ++int nand_default_bbt (struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ /* Default for AG-AND. We must use a flash based ++ * bad block table as the devices have factory marked ++ * _good_ blocks. Erasing those blocks leads to loss ++ * of the good / bad information, so we _must_ store ++ * this information in a good / bad table during ++ * startup ++ */ ++ if (this->options & NAND_IS_AND) { ++ /* Use the default pattern descriptors */ ++ if (!this->bbt_td) { ++ this->bbt_td = &bbt_main_descr; ++ this->bbt_md = &bbt_mirror_descr; ++ } ++ this->options |= NAND_USE_FLASH_BBT; ++ return nand_scan_bbt (mtd, &agand_flashbased); ++ } ++ ++ ++ /* Is a flash based bad block table requested ? */ ++ if (this->options & NAND_USE_FLASH_BBT) { ++ /* Use the default pattern descriptors */ ++ if (!this->bbt_td) { ++ this->bbt_td = &bbt_main_descr; ++ this->bbt_md = &bbt_mirror_descr; ++ } ++ if (!this->badblock_pattern) { ++ this->badblock_pattern = (mtd->oobblock > 512) ? ++ &largepage_flashbased : &smallpage_flashbased; ++ } ++ } else { ++ this->bbt_td = NULL; ++ this->bbt_md = NULL; ++ if (!this->badblock_pattern) { ++ this->badblock_pattern = (mtd->oobblock > 512) ? ++ &largepage_memorybased : &smallpage_memorybased; ++ } ++ } ++ return nand_scan_bbt (mtd, this->badblock_pattern); ++} ++ ++/** ++ * nand_isbad_bbt - [NAND Interface] Check if a block is bad ++ * @mtd: MTD device structure ++ * @offs: offset in the device ++ * @allowbbt: allow access to bad block table region ++ * ++*/ ++int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) ++{ ++ struct nand_chip *this = mtd->priv; ++ int block; ++ uint8_t res; ++ ++ /* Get block number * 2 */ ++ block = (int) (offs >> (this->bbt_erase_shift - 1)); ++ res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; ++ ++ DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", ++ (unsigned int)offs, block >> 1, res); ++ ++ switch ((int)res) { ++ case 0x00: return 0; ++ case 0x01: return 1; ++ case 0x02: return allowbbt ? 0 : 1; ++ } ++ return 1; ++} ++ ++EXPORT_SYMBOL (nand_scan_bbt); ++EXPORT_SYMBOL (nand_default_bbt); +--- linux-2.4.21/drivers/mtd/nand/nand_ecc.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/nand_ecc.c +@@ -1,22 +1,44 @@ + /* +- * drivers/mtd/nand_ecc.c ++ * This file contains an ECC algorithm from Toshiba that detects and ++ * corrects 1 bit errors in a 256 byte block of data. + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * drivers/mtd/nand/nand_ecc.c ++ * ++ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * +- * $Id$ ++ * $Id$ + * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public License +- * version 2.1 as published by the Free Software Foundation. ++ * This file 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 or (at your option) any ++ * later version. + * +- * This file contains an ECC algorithm from Toshiba that detects and +- * corrects 1 bit errors in a 256 byte block of data. ++ * This file 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 file; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * As a special exception, if other files instantiate templates or use ++ * macros or inline functions from these files, or you compile these ++ * files and link them with other works to produce a work based on these ++ * files, these files do not by themselves cause the resulting work to be ++ * covered by the GNU General Public License. However the source code for ++ * these files must still be made available in accordance with section (3) ++ * of the GNU General Public License. ++ * ++ * This exception does not invalidate any other reasons why a work based on ++ * this file might be covered by the GNU General Public License. + */ + + #include + #include + #include ++#include + + /* + * Pre-calculated 256-way 1 byte column parity +@@ -41,7 +63,12 @@ + }; + + +-/* ++/** ++ * nand_trans_result - [GENERIC] create non-inverted ECC ++ * @reg2: line parity reg 2 ++ * @reg3: line parity reg 3 ++ * @ecc_code: ecc ++ * + * Creates non-inverted ECC code from line parity + */ + static void nand_trans_result(u_char reg2, u_char reg3, +@@ -81,10 +108,13 @@ + ecc_code[1] = tmp2; + } + +-/* +- * Calculate 3 byte ECC code for 256 byte block ++/** ++ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block ++ * @mtd: MTD block structure ++ * @dat: raw data ++ * @ecc_code: buffer for ECC + */ +-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) ++int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) + { + u_char idx, reg1, reg2, reg3; + int j; +@@ -114,12 +144,19 @@ + ecc_code[0] = ~ecc_code[0]; + ecc_code[1] = ~ecc_code[1]; + ecc_code[2] = ((~reg1) << 2) | 0x03; ++ return 0; + } + +-/* ++/** ++ * nand_correct_data - [NAND Interface] Detect and correct bit error(s) ++ * @mtd: MTD block structure ++ * @dat: raw data read from the chip ++ * @read_ecc: ECC from the chip ++ * @calc_ecc: the ECC calculated from raw data ++ * + * Detect and correct a 1 bit error for 256 byte block + */ +-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) ++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) + { + u_char a, b, c, d1, d2, d3, add, bit, i; + +@@ -209,5 +246,5 @@ + EXPORT_SYMBOL(nand_correct_data); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill "); ++MODULE_AUTHOR("Steven J. Hill "); + MODULE_DESCRIPTION("Generic NAND ECC support"); +--- linux-2.4.21/drivers/mtd/nand/nand_ids.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/nand_ids.c +@@ -3,8 +3,7 @@ + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * +- * +- * $Id$ ++ * $Id$ + * + * 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 +@@ -13,26 +12,97 @@ + */ + #include + #include +- + /* + * Chip ID list ++* ++* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, ++* options ++* ++* Pagesize; 0, 256, 512 ++* 0 get this information from the extended chip ID +++ 256 256 Byte page size ++* 512 512 Byte page size + */ + struct nand_flash_dev nand_flash_ids[] = { +- {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V +- {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V +- {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V +- {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V +- {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V +- {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V +- {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V +- {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V ++ {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, ++ {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, ++ {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, ++ {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, ++ {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, ++ {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, ++ {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, ++ {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, ++ {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, ++ {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, ++ ++ {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, ++ {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, ++ {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, ++ {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, ++ ++ {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, ++ {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, ++ {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, ++ {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, ++ ++ {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, ++ {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, ++ {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, ++ {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, ++ ++ {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, ++ {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, ++ {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, ++ {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, ++ ++ {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, ++ {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, ++ {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, ++ {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, ++ ++ {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, ++ ++ /* These are the new chips with large page size. The pagesize ++ * and the erasesize is determined from the extended id bytes ++ */ ++ /* 1 Gigabit */ ++ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ ++ /* 2 Gigabit */ ++ {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ ++ /* 4 Gigabit */ ++ {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ ++ /* 8 Gigabit */ ++ {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ ++ /* 16 Gigabit */ ++ {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, ++ {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, ++ ++ /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! ++ * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes ++ * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 ++ * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go ++ * There are more speed improvements for reads and writes possible, but not implemented now ++ */ ++ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, ++ + {NULL,} + }; + +@@ -44,10 +114,11 @@ + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, ++ {NAND_MFR_RENESAS, "Renesas"}, ++ {NAND_MFR_STMICRO, "ST Micro"}, + {0x0, "Unknown"} + }; + +- + EXPORT_SYMBOL (nand_manuf_ids); + EXPORT_SYMBOL (nand_flash_ids); + +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/nandsim.c +@@ -0,0 +1,1613 @@ ++/* ++ * NAND flash simulator. ++ * ++ * Author: Artem B. Bityuckiy , ++ * ++ * Copyright (C) 2004 Nokia Corporation ++ * ++ * Note: NS means "NAND Simulator". ++ * Note: Input means input TO flash chip, output means output FROM chip. ++ * ++ * 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, or (at your option) any later ++ * version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General ++ * Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_NS_ABS_POS ++#include ++#endif ++ ++ ++/* Default simulator parameters values */ ++#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) ++#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 ++#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 ++#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ ++#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ ++#endif ++ ++#ifndef CONFIG_NANDSIM_ACCESS_DELAY ++#define CONFIG_NANDSIM_ACCESS_DELAY 25 ++#endif ++#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY ++#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 ++#endif ++#ifndef CONFIG_NANDSIM_ERASE_DELAY ++#define CONFIG_NANDSIM_ERASE_DELAY 2 ++#endif ++#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE ++#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 ++#endif ++#ifndef CONFIG_NANDSIM_INPUT_CYCLE ++#define CONFIG_NANDSIM_INPUT_CYCLE 50 ++#endif ++#ifndef CONFIG_NANDSIM_BUS_WIDTH ++#define CONFIG_NANDSIM_BUS_WIDTH 8 ++#endif ++#ifndef CONFIG_NANDSIM_DO_DELAYS ++#define CONFIG_NANDSIM_DO_DELAYS 0 ++#endif ++#ifndef CONFIG_NANDSIM_LOG ++#define CONFIG_NANDSIM_LOG 0 ++#endif ++#ifndef CONFIG_NANDSIM_DBG ++#define CONFIG_NANDSIM_DBG 0 ++#endif ++ ++static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; ++static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; ++static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE; ++static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE; ++static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; ++static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; ++static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; ++static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; ++static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; ++static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; ++static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; ++static uint log = CONFIG_NANDSIM_LOG; ++static uint dbg = CONFIG_NANDSIM_DBG; ++ ++module_param(first_id_byte, uint, 0400); ++module_param(second_id_byte, uint, 0400); ++module_param(third_id_byte, uint, 0400); ++module_param(fourth_id_byte, uint, 0400); ++module_param(access_delay, uint, 0400); ++module_param(programm_delay, uint, 0400); ++module_param(erase_delay, uint, 0400); ++module_param(output_cycle, uint, 0400); ++module_param(input_cycle, uint, 0400); ++module_param(bus_width, uint, 0400); ++module_param(do_delays, uint, 0400); ++module_param(log, uint, 0400); ++module_param(dbg, uint, 0400); ++ ++MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); ++MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); ++MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); ++MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); ++MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); ++MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); ++MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); ++MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); ++MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); ++MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); ++MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); ++MODULE_PARM_DESC(log, "Perform logging if not zero"); ++MODULE_PARM_DESC(dbg, "Output debug information if not zero"); ++ ++/* The largest possible page size */ ++#define NS_LARGEST_PAGE_SIZE 2048 ++ ++/* The prefix for simulator output */ ++#define NS_OUTPUT_PREFIX "[nandsim]" ++ ++/* Simulator's output macros (logging, debugging, warning, error) */ ++#define NS_LOG(args...) \ ++ do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) ++#define NS_DBG(args...) \ ++ do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) ++#define NS_WARN(args...) \ ++ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) ++#define NS_ERR(args...) \ ++ do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) ++ ++/* Busy-wait delay macros (microseconds, milliseconds) */ ++#define NS_UDELAY(us) \ ++ do { if (do_delays) udelay(us); } while(0) ++#define NS_MDELAY(us) \ ++ do { if (do_delays) mdelay(us); } while(0) ++ ++/* Is the nandsim structure initialized ? */ ++#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) ++ ++/* Good operation completion status */ ++#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) ++ ++/* Operation failed completion status */ ++#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) ++ ++/* Calculate the page offset in flash RAM image by (row, column) address */ ++#define NS_RAW_OFFSET(ns) \ ++ (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) ++ ++/* Calculate the OOB offset in flash RAM image by (row, column) address */ ++#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) ++ ++/* After a command is input, the simulator goes to one of the following states */ ++#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ ++#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ ++#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ ++#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */ ++#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ ++#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ ++#define STATE_CMD_STATUS 0x00000007 /* read status */ ++#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */ ++#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */ ++#define STATE_CMD_READID 0x0000000A /* read ID */ ++#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ ++#define STATE_CMD_RESET 0x0000000C /* reset */ ++#define STATE_CMD_MASK 0x0000000F /* command states mask */ ++ ++/* After an addres is input, the simulator goes to one of these states */ ++#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ ++#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ ++#define STATE_ADDR_ZERO 0x00000030 /* one byte zero address was accepted */ ++#define STATE_ADDR_MASK 0x00000030 /* address states mask */ ++ ++/* Durind data input/output the simulator is in these states */ ++#define STATE_DATAIN 0x00000100 /* waiting for data input */ ++#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ ++ ++#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ ++#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ ++#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ ++#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ ++#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ ++ ++/* Previous operation is done, ready to accept new requests */ ++#define STATE_READY 0x00000000 ++ ++/* This state is used to mark that the next state isn't known yet */ ++#define STATE_UNKNOWN 0x10000000 ++ ++/* Simulator's actions bit masks */ ++#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ ++#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */ ++#define ACTION_SECERASE 0x00300000 /* erase sector */ ++#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ ++#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ ++#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ ++#define ACTION_MASK 0x00700000 /* action mask */ ++ ++#define NS_OPER_NUM 12 /* Number of operations supported by the simulator */ ++#define NS_OPER_STATES 6 /* Maximum number of states in operation */ ++ ++#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ ++#define OPT_PAGE256 0x00000001 /* 256-byte page chips */ ++#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ ++#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ ++#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ ++#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ ++#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ ++#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ ++#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ ++ ++/* Remove action bits ftom state */ ++#define NS_STATE(x) ((x) & ~ACTION_MASK) ++ ++/* ++ * Maximum previous states which need to be saved. Currently saving is ++ * only needed for page programm operation with preceeded read command ++ * (which is only valid for 512-byte pages). ++ */ ++#define NS_MAX_PREVSTATES 1 ++ ++/* ++ * The structure which describes all the internal simulator data. ++ */ ++struct nandsim { ++ struct mtd_partition part; ++ ++ uint busw; /* flash chip bus width (8 or 16) */ ++ u_char ids[4]; /* chip's ID bytes */ ++ uint32_t options; /* chip's characteristic bits */ ++ uint32_t state; /* current chip state */ ++ uint32_t nxstate; /* next expected state */ ++ ++ uint32_t *op; /* current operation, NULL operations isn't known yet */ ++ uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ ++ uint16_t npstates; /* number of previous states saved */ ++ uint16_t stateidx; /* current state index */ ++ ++ /* The simulated NAND flash image */ ++ union flash_media { ++ u_char *byte; ++ uint16_t *word; ++ } mem; ++ ++ /* Internal buffer of page + OOB size bytes */ ++ union internal_buffer { ++ u_char *byte; /* for byte access */ ++ uint16_t *word; /* for 16-bit word access */ ++ } buf; ++ ++ /* NAND flash "geometry" */ ++ struct nandsin_geometry { ++ uint32_t totsz; /* total flash size, bytes */ ++ uint32_t secsz; /* flash sector (erase block) size, bytes */ ++ uint pgsz; /* NAND flash page size, bytes */ ++ uint oobsz; /* page OOB area size, bytes */ ++ uint32_t totszoob; /* total flash size including OOB, bytes */ ++ uint pgszoob; /* page size including OOB , bytes*/ ++ uint secszoob; /* sector size including OOB, bytes */ ++ uint pgnum; /* total number of pages */ ++ uint pgsec; /* number of pages per sector */ ++ uint secshift; /* bits number in sector size */ ++ uint pgshift; /* bits number in page size */ ++ uint oobshift; /* bits number in OOB size */ ++ uint pgaddrbytes; /* bytes per page address */ ++ uint secaddrbytes; /* bytes per sector address */ ++ uint idbytes; /* the number ID bytes that this chip outputs */ ++ } geom; ++ ++ /* NAND flash internal registers */ ++ struct nandsim_regs { ++ unsigned command; /* the command register */ ++ u_char status; /* the status register */ ++ uint row; /* the page number */ ++ uint column; /* the offset within page */ ++ uint count; /* internal counter */ ++ uint num; /* number of bytes which must be processed */ ++ uint off; /* fixed page offset */ ++ } regs; ++ ++ /* NAND flash lines state */ ++ struct ns_lines_status { ++ int ce; /* chip Enable */ ++ int cle; /* command Latch Enable */ ++ int ale; /* address Latch Enable */ ++ int wp; /* write Protect */ ++ } lines; ++}; ++ ++/* ++ * Operations array. To perform any operation the simulator must pass ++ * through the correspondent states chain. ++ */ ++static struct nandsim_operations { ++ uint32_t reqopts; /* options which are required to perform the operation */ ++ uint32_t states[NS_OPER_STATES]; /* operation's states */ ++} ops[NS_OPER_NUM] = { ++ /* Read page + OOB from the beginning */ ++ {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Read page + OOB from the second half */ ++ {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Read OOB */ ++ {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Programm page starting from the beginning */ ++ {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, ++ STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Programm page starting from the beginning */ ++ {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Programm page starting from the second half */ ++ {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Programm OOB */ ++ {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Erase sector */ ++ {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, ++ /* Read status */ ++ {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, ++ /* Read multi-plane status */ ++ {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, ++ /* Read ID */ ++ {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, ++ /* Large page devices read page */ ++ {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}} ++}; ++ ++/* MTD structure for NAND controller */ ++static struct mtd_info *nsmtd; ++ ++static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; ++ ++/* ++ * Initialize the nandsim structure. ++ * ++ * RETURNS: 0 if success, -ERRNO if failure. ++ */ ++static int ++init_nandsim(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ struct nandsim *ns = (struct nandsim *)(chip->priv); ++ int i; ++ ++ if (NS_IS_INITIALIZED(ns)) { ++ NS_ERR("init_nandsim: nandsim is already initialized\n"); ++ return -EIO; ++ } ++ ++ /* Force mtd to not do delays */ ++ chip->chip_delay = 0; ++ ++ /* Initialize the NAND flash parameters */ ++ ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; ++ ns->geom.totsz = mtd->size; ++ ns->geom.pgsz = mtd->oobblock; ++ ns->geom.oobsz = mtd->oobsize; ++ ns->geom.secsz = mtd->erasesize; ++ ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; ++ ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; ++ ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; ++ ns->geom.secshift = ffs(ns->geom.secsz) - 1; ++ ns->geom.pgshift = chip->page_shift; ++ ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; ++ ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ++ ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ++ ns->options = 0; ++ ++ if (ns->geom.pgsz == 256) { ++ ns->options |= OPT_PAGE256; ++ } ++ else if (ns->geom.pgsz == 512) { ++ ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); ++ if (ns->busw == 8) ++ ns->options |= OPT_PAGE512_8BIT; ++ } else if (ns->geom.pgsz == 2048) { ++ ns->options |= OPT_PAGE2048; ++ } else { ++ NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); ++ return -EIO; ++ } ++ ++ if (ns->options & OPT_SMALLPAGE) { ++ if (ns->geom.totsz < (64 << 20)) { ++ ns->geom.pgaddrbytes = 3; ++ ns->geom.secaddrbytes = 2; ++ } else { ++ ns->geom.pgaddrbytes = 4; ++ ns->geom.secaddrbytes = 3; ++ } ++ } else { ++ if (ns->geom.totsz <= (128 << 20)) { ++ ns->geom.pgaddrbytes = 5; ++ ns->geom.secaddrbytes = 2; ++ } else { ++ ns->geom.pgaddrbytes = 5; ++ ns->geom.secaddrbytes = 3; ++ } ++ } ++ ++ /* Detect how many ID bytes the NAND chip outputs */ ++ for (i = 0; nand_flash_ids[i].name != NULL; i++) { ++ if (second_id_byte != nand_flash_ids[i].id) ++ continue; ++ if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) ++ ns->options |= OPT_AUTOINCR; ++ } ++ ++ if (ns->busw == 16) ++ NS_WARN("16-bit flashes support wasn't tested\n"); ++ ++ printk("flash size: %u MiB\n", ns->geom.totsz >> 20); ++ printk("page size: %u bytes\n", ns->geom.pgsz); ++ printk("OOB area size: %u bytes\n", ns->geom.oobsz); ++ printk("sector size: %u KiB\n", ns->geom.secsz >> 10); ++ printk("pages number: %u\n", ns->geom.pgnum); ++ printk("pages per sector: %u\n", ns->geom.pgsec); ++ printk("bus width: %u\n", ns->busw); ++ printk("bits in sector size: %u\n", ns->geom.secshift); ++ printk("bits in page size: %u\n", ns->geom.pgshift); ++ printk("bits in OOB size: %u\n", ns->geom.oobshift); ++ printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); ++ printk("page address bytes: %u\n", ns->geom.pgaddrbytes); ++ printk("sector address bytes: %u\n", ns->geom.secaddrbytes); ++ printk("options: %#x\n", ns->options); ++ ++ /* Map / allocate and initialize the flash image */ ++#ifdef CONFIG_NS_ABS_POS ++ ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); ++ if (!ns->mem.byte) { ++ NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", ++ (void *)CONFIG_NS_ABS_POS); ++ return -ENOMEM; ++ } ++#else ++ ns->mem.byte = vmalloc(ns->geom.totszoob); ++ if (!ns->mem.byte) { ++ NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", ++ ns->geom.totszoob); ++ return -ENOMEM; ++ } ++ memset(ns->mem.byte, 0xFF, ns->geom.totszoob); ++#endif ++ ++ /* Allocate / initialize the internal buffer */ ++ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); ++ if (!ns->buf.byte) { ++ NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ++ ns->geom.pgszoob); ++ goto error; ++ } ++ memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); ++ ++ /* Fill the partition_info structure */ ++ ns->part.name = "NAND simulator partition"; ++ ns->part.offset = 0; ++ ns->part.size = ns->geom.totsz; ++ ++ return 0; ++ ++error: ++#ifdef CONFIG_NS_ABS_POS ++ iounmap(ns->mem.byte); ++#else ++ vfree(ns->mem.byte); ++#endif ++ ++ return -ENOMEM; ++} ++ ++/* ++ * Free the nandsim structure. ++ */ ++static void ++free_nandsim(struct nandsim *ns) ++{ ++ kfree(ns->buf.byte); ++ ++#ifdef CONFIG_NS_ABS_POS ++ iounmap(ns->mem.byte); ++#else ++ vfree(ns->mem.byte); ++#endif ++ ++ return; ++} ++ ++/* ++ * Returns the string representation of 'state' state. ++ */ ++static char * ++get_state_name(uint32_t state) ++{ ++ switch (NS_STATE(state)) { ++ case STATE_CMD_READ0: ++ return "STATE_CMD_READ0"; ++ case STATE_CMD_READ1: ++ return "STATE_CMD_READ1"; ++ case STATE_CMD_PAGEPROG: ++ return "STATE_CMD_PAGEPROG"; ++ case STATE_CMD_READOOB: ++ return "STATE_CMD_READOOB"; ++ case STATE_CMD_READSTART: ++ return "STATE_CMD_READSTART"; ++ case STATE_CMD_ERASE1: ++ return "STATE_CMD_ERASE1"; ++ case STATE_CMD_STATUS: ++ return "STATE_CMD_STATUS"; ++ case STATE_CMD_STATUS_M: ++ return "STATE_CMD_STATUS_M"; ++ case STATE_CMD_SEQIN: ++ return "STATE_CMD_SEQIN"; ++ case STATE_CMD_READID: ++ return "STATE_CMD_READID"; ++ case STATE_CMD_ERASE2: ++ return "STATE_CMD_ERASE2"; ++ case STATE_CMD_RESET: ++ return "STATE_CMD_RESET"; ++ case STATE_ADDR_PAGE: ++ return "STATE_ADDR_PAGE"; ++ case STATE_ADDR_SEC: ++ return "STATE_ADDR_SEC"; ++ case STATE_ADDR_ZERO: ++ return "STATE_ADDR_ZERO"; ++ case STATE_DATAIN: ++ return "STATE_DATAIN"; ++ case STATE_DATAOUT: ++ return "STATE_DATAOUT"; ++ case STATE_DATAOUT_ID: ++ return "STATE_DATAOUT_ID"; ++ case STATE_DATAOUT_STATUS: ++ return "STATE_DATAOUT_STATUS"; ++ case STATE_DATAOUT_STATUS_M: ++ return "STATE_DATAOUT_STATUS_M"; ++ case STATE_READY: ++ return "STATE_READY"; ++ case STATE_UNKNOWN: ++ return "STATE_UNKNOWN"; ++ } ++ ++ NS_ERR("get_state_name: unknown state, BUG\n"); ++ return NULL; ++} ++ ++/* ++ * Check if command is valid. ++ * ++ * RETURNS: 1 if wrong command, 0 if right. ++ */ ++static int ++check_command(int cmd) ++{ ++ switch (cmd) { ++ ++ case NAND_CMD_READ0: ++ case NAND_CMD_READSTART: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_READOOB: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_READID: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_RESET: ++ case NAND_CMD_READ1: ++ return 0; ++ ++ case NAND_CMD_STATUS_MULTI: ++ default: ++ return 1; ++ } ++} ++ ++/* ++ * Returns state after command is accepted by command number. ++ */ ++static uint32_t ++get_state_by_command(unsigned command) ++{ ++ switch (command) { ++ case NAND_CMD_READ0: ++ return STATE_CMD_READ0; ++ case NAND_CMD_READ1: ++ return STATE_CMD_READ1; ++ case NAND_CMD_PAGEPROG: ++ return STATE_CMD_PAGEPROG; ++ case NAND_CMD_READSTART: ++ return STATE_CMD_READSTART; ++ case NAND_CMD_READOOB: ++ return STATE_CMD_READOOB; ++ case NAND_CMD_ERASE1: ++ return STATE_CMD_ERASE1; ++ case NAND_CMD_STATUS: ++ return STATE_CMD_STATUS; ++ case NAND_CMD_STATUS_MULTI: ++ return STATE_CMD_STATUS_M; ++ case NAND_CMD_SEQIN: ++ return STATE_CMD_SEQIN; ++ case NAND_CMD_READID: ++ return STATE_CMD_READID; ++ case NAND_CMD_ERASE2: ++ return STATE_CMD_ERASE2; ++ case NAND_CMD_RESET: ++ return STATE_CMD_RESET; ++ } ++ ++ NS_ERR("get_state_by_command: unknown command, BUG\n"); ++ return 0; ++} ++ ++/* ++ * Move an address byte to the correspondent internal register. ++ */ ++static inline void ++accept_addr_byte(struct nandsim *ns, u_char bt) ++{ ++ uint byte = (uint)bt; ++ ++ if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) ++ ns->regs.column |= (byte << 8 * ns->regs.count); ++ else { ++ ns->regs.row |= (byte << 8 * (ns->regs.count - ++ ns->geom.pgaddrbytes + ++ ns->geom.secaddrbytes)); ++ } ++ ++ return; ++} ++ ++/* ++ * Switch to STATE_READY state. ++ */ ++static inline void ++switch_to_ready_state(struct nandsim *ns, u_char status) ++{ ++ NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); ++ ++ ns->state = STATE_READY; ++ ns->nxstate = STATE_UNKNOWN; ++ ns->op = NULL; ++ ns->npstates = 0; ++ ns->stateidx = 0; ++ ns->regs.num = 0; ++ ns->regs.count = 0; ++ ns->regs.off = 0; ++ ns->regs.row = 0; ++ ns->regs.column = 0; ++ ns->regs.status = status; ++} ++ ++/* ++ * If the operation isn't known yet, try to find it in the global array ++ * of supported operations. ++ * ++ * Operation can be unknown because of the following. ++ * 1. New command was accepted and this is the firs call to find the ++ * correspondent states chain. In this case ns->npstates = 0; ++ * 2. There is several operations which begin with the same command(s) ++ * (for example program from the second half and read from the ++ * second half operations both begin with the READ1 command). In this ++ * case the ns->pstates[] array contains previous states. ++ * ++ * Thus, the function tries to find operation containing the following ++ * states (if the 'flag' parameter is 0): ++ * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state ++ * ++ * If (one and only one) matching operation is found, it is accepted ( ++ * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is ++ * zeroed). ++ * ++ * If there are several maches, the current state is pushed to the ++ * ns->pstates. ++ * ++ * The operation can be unknown only while commands are input to the chip. ++ * As soon as address command is accepted, the operation must be known. ++ * In such situation the function is called with 'flag' != 0, and the ++ * operation is searched using the following pattern: ++ * ns->pstates[0], ... ns->pstates[ns->npstates],
++ * ++ * It is supposed that this pattern must either match one operation on ++ * none. There can't be ambiguity in that case. ++ * ++ * If no matches found, the functions does the following: ++ * 1. if there are saved states present, try to ignore them and search ++ * again only using the last command. If nothing was found, switch ++ * to the STATE_READY state. ++ * 2. if there are no saved states, switch to the STATE_READY state. ++ * ++ * RETURNS: -2 - no matched operations found. ++ * -1 - several matches. ++ * 0 - operation is found. ++ */ ++static int ++find_operation(struct nandsim *ns, uint32_t flag) ++{ ++ int opsfound = 0; ++ int i, j, idx = 0; ++ ++ for (i = 0; i < NS_OPER_NUM; i++) { ++ ++ int found = 1; ++ ++ if (!(ns->options & ops[i].reqopts)) ++ /* Ignore operations we can't perform */ ++ continue; ++ ++ if (flag) { ++ if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) ++ continue; ++ } else { ++ if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) ++ continue; ++ } ++ ++ for (j = 0; j < ns->npstates; j++) ++ if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) ++ && (ns->options & ops[idx].reqopts)) { ++ found = 0; ++ break; ++ } ++ ++ if (found) { ++ idx = i; ++ opsfound += 1; ++ } ++ } ++ ++ if (opsfound == 1) { ++ /* Exact match */ ++ ns->op = &ops[idx].states[0]; ++ if (flag) { ++ /* ++ * In this case the find_operation function was ++ * called when address has just began input. But it isn't ++ * yet fully input and the current state must ++ * not be one of STATE_ADDR_*, but the STATE_ADDR_* ++ * state must be the next state (ns->nxstate). ++ */ ++ ns->stateidx = ns->npstates - 1; ++ } else { ++ ns->stateidx = ns->npstates; ++ } ++ ns->npstates = 0; ++ ns->state = ns->op[ns->stateidx]; ++ ns->nxstate = ns->op[ns->stateidx + 1]; ++ NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", ++ idx, get_state_name(ns->state), get_state_name(ns->nxstate)); ++ return 0; ++ } ++ ++ if (opsfound == 0) { ++ /* Nothing was found. Try to ignore previous commands (if any) and search again */ ++ if (ns->npstates != 0) { ++ NS_DBG("find_operation: no operation found, try again with state %s\n", ++ get_state_name(ns->state)); ++ ns->npstates = 0; ++ return find_operation(ns, 0); ++ ++ } ++ NS_DBG("find_operation: no operations found\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return -2; ++ } ++ ++ if (flag) { ++ /* This shouldn't happen */ ++ NS_DBG("find_operation: BUG, operation must be known if address is input\n"); ++ return -2; ++ } ++ ++ NS_DBG("find_operation: there is still ambiguity\n"); ++ ++ ns->pstates[ns->npstates++] = ns->state; ++ ++ return -1; ++} ++ ++/* ++ * If state has any action bit, perform this action. ++ * ++ * RETURNS: 0 if success, -1 if error. ++ */ ++static int ++do_state_action(struct nandsim *ns, uint32_t action) ++{ ++ int i, num; ++ int busdiv = ns->busw == 8 ? 1 : 2; ++ ++ action &= ACTION_MASK; ++ ++ /* Check that page address input is correct */ ++ if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { ++ NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); ++ return -1; ++ } ++ ++ switch (action) { ++ ++ case ACTION_CPY: ++ /* ++ * Copy page data to the internal buffer. ++ */ ++ ++ /* Column shouldn't be very large */ ++ if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { ++ NS_ERR("do_state_action: column number is too large\n"); ++ break; ++ } ++ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); ++ ++ NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", ++ num, NS_RAW_OFFSET(ns) + ns->regs.off); ++ ++ if (ns->regs.off == 0) ++ NS_LOG("read page %d\n", ns->regs.row); ++ else if (ns->regs.off < ns->geom.pgsz) ++ NS_LOG("read page %d (second half)\n", ns->regs.row); ++ else ++ NS_LOG("read OOB of page %d\n", ns->regs.row); ++ ++ NS_UDELAY(access_delay); ++ NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); ++ ++ break; ++ ++ case ACTION_SECERASE: ++ /* ++ * Erase sector. ++ */ ++ ++ if (ns->lines.wp) { ++ NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); ++ return -1; ++ } ++ ++ if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec ++ || (ns->regs.row & ~(ns->geom.secsz - 1))) { ++ NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); ++ return -1; ++ } ++ ++ ns->regs.row = (ns->regs.row << ++ 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; ++ ns->regs.column = 0; ++ ++ NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", ++ ns->regs.row, NS_RAW_OFFSET(ns)); ++ NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); ++ ++ memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); ++ ++ NS_MDELAY(erase_delay); ++ ++ break; ++ ++ case ACTION_PRGPAGE: ++ /* ++ * Programm page - move internal buffer data to the page. ++ */ ++ ++ if (ns->lines.wp) { ++ NS_WARN("do_state_action: device is write-protected, programm\n"); ++ return -1; ++ } ++ ++ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ if (num != ns->regs.count) { ++ NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", ++ ns->regs.count, num); ++ return -1; ++ } ++ ++ for (i = 0; i < num; i++) ++ ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; ++ ++ NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", ++ num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); ++ NS_LOG("programm page %d\n", ns->regs.row); ++ ++ NS_UDELAY(programm_delay); ++ NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); ++ ++ break; ++ ++ case ACTION_ZEROOFF: ++ NS_DBG("do_state_action: set internal offset to 0\n"); ++ ns->regs.off = 0; ++ break; ++ ++ case ACTION_HALFOFF: ++ if (!(ns->options & OPT_PAGE512_8BIT)) { ++ NS_ERR("do_state_action: BUG! can't skip half of page for non-512" ++ "byte page size 8x chips\n"); ++ return -1; ++ } ++ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); ++ ns->regs.off = ns->geom.pgsz/2; ++ break; ++ ++ case ACTION_OOBOFF: ++ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); ++ ns->regs.off = ns->geom.pgsz; ++ break; ++ ++ default: ++ NS_DBG("do_state_action: BUG! unknown action\n"); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Switch simulator's state. ++ */ ++static void ++switch_state(struct nandsim *ns) ++{ ++ if (ns->op) { ++ /* ++ * The current operation have already been identified. ++ * Just follow the states chain. ++ */ ++ ++ ns->stateidx += 1; ++ ns->state = ns->nxstate; ++ ns->nxstate = ns->op[ns->stateidx + 1]; ++ ++ NS_DBG("switch_state: operation is known, switch to the next state, " ++ "state: %s, nxstate: %s\n", ++ get_state_name(ns->state), get_state_name(ns->nxstate)); ++ ++ /* See, whether we need to do some action */ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ } else { ++ /* ++ * We don't yet know which operation we perform. ++ * Try to identify it. ++ */ ++ ++ /* ++ * The only event causing the switch_state function to ++ * be called with yet unknown operation is new command. ++ */ ++ ns->state = get_state_by_command(ns->regs.command); ++ ++ NS_DBG("switch_state: operation is unknown, try to find it\n"); ++ ++ if (find_operation(ns, 0) != 0) ++ return; ++ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ } ++ ++ /* For 16x devices column means the page offset in words */ ++ if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { ++ NS_DBG("switch_state: double the column number for 16x device\n"); ++ ns->regs.column <<= 1; ++ } ++ ++ if (NS_STATE(ns->nxstate) == STATE_READY) { ++ /* ++ * The current state is the last. Return to STATE_READY ++ */ ++ ++ u_char status = NS_STATUS_OK(ns); ++ ++ /* In case of data states, see if all bytes were input/output */ ++ if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) ++ && ns->regs.count != ns->regs.num) { ++ NS_WARN("switch_state: not all bytes were processed, %d left\n", ++ ns->regs.num - ns->regs.count); ++ status = NS_STATUS_FAILED(ns); ++ } ++ ++ NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); ++ ++ switch_to_ready_state(ns, status); ++ ++ return; ++ } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { ++ /* ++ * If the next state is data input/output, switch to it now ++ */ ++ ++ ns->state = ns->nxstate; ++ ns->nxstate = ns->op[++ns->stateidx + 1]; ++ ns->regs.num = ns->regs.count = 0; ++ ++ NS_DBG("switch_state: the next state is data I/O, switch, " ++ "state: %s, nxstate: %s\n", ++ get_state_name(ns->state), get_state_name(ns->nxstate)); ++ ++ /* ++ * Set the internal register to the count of bytes which ++ * are expected to be input or output ++ */ ++ switch (NS_STATE(ns->state)) { ++ case STATE_DATAIN: ++ case STATE_DATAOUT: ++ ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ break; ++ ++ case STATE_DATAOUT_ID: ++ ns->regs.num = ns->geom.idbytes; ++ break; ++ ++ case STATE_DATAOUT_STATUS: ++ case STATE_DATAOUT_STATUS_M: ++ ns->regs.count = ns->regs.num = 0; ++ break; ++ ++ default: ++ NS_ERR("switch_state: BUG! unknown data state\n"); ++ } ++ ++ } else if (ns->nxstate & STATE_ADDR_MASK) { ++ /* ++ * If the next state is address input, set the internal ++ * register to the number of expected address bytes ++ */ ++ ++ ns->regs.count = 0; ++ ++ switch (NS_STATE(ns->nxstate)) { ++ case STATE_ADDR_PAGE: ++ ns->regs.num = ns->geom.pgaddrbytes; ++ ++ break; ++ case STATE_ADDR_SEC: ++ ns->regs.num = ns->geom.secaddrbytes; ++ break; ++ ++ case STATE_ADDR_ZERO: ++ ns->regs.num = 1; ++ break; ++ ++ default: ++ NS_ERR("switch_state: BUG! unknown address state\n"); ++ } ++ } else { ++ /* ++ * Just reset internal counters. ++ */ ++ ++ ns->regs.num = 0; ++ ns->regs.count = 0; ++ } ++} ++ ++static void ++ns_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; ++ ++ switch (cmd) { ++ ++ /* set CLE line high */ ++ case NAND_CTL_SETCLE: ++ NS_DBG("ns_hwcontrol: start command latch cycles\n"); ++ ns->lines.cle = 1; ++ break; ++ ++ /* set CLE line low */ ++ case NAND_CTL_CLRCLE: ++ NS_DBG("ns_hwcontrol: stop command latch cycles\n"); ++ ns->lines.cle = 0; ++ break; ++ ++ /* set ALE line high */ ++ case NAND_CTL_SETALE: ++ NS_DBG("ns_hwcontrol: start address latch cycles\n"); ++ ns->lines.ale = 1; ++ break; ++ ++ /* set ALE line low */ ++ case NAND_CTL_CLRALE: ++ NS_DBG("ns_hwcontrol: stop address latch cycles\n"); ++ ns->lines.ale = 0; ++ break; ++ ++ /* set WP line high */ ++ case NAND_CTL_SETWP: ++ NS_DBG("ns_hwcontrol: enable write protection\n"); ++ ns->lines.wp = 1; ++ break; ++ ++ /* set WP line low */ ++ case NAND_CTL_CLRWP: ++ NS_DBG("ns_hwcontrol: disable write protection\n"); ++ ns->lines.wp = 0; ++ break; ++ ++ /* set CE line low */ ++ case NAND_CTL_SETNCE: ++ NS_DBG("ns_hwcontrol: enable chip\n"); ++ ns->lines.ce = 1; ++ break; ++ ++ /* set CE line high */ ++ case NAND_CTL_CLRNCE: ++ NS_DBG("ns_hwcontrol: disable chip\n"); ++ ns->lines.ce = 0; ++ break; ++ ++ default: ++ NS_ERR("hwcontrol: unknown command\n"); ++ } ++ ++ return; ++} ++ ++static u_char ++ns_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; ++ u_char outb = 0x00; ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); ++ return outb; ++ } ++ if (ns->lines.ale || ns->lines.cle) { ++ NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); ++ return outb; ++ } ++ if (!(ns->state & STATE_DATAOUT_MASK)) { ++ NS_WARN("read_byte: unexpected data output cycle, state is %s " ++ "return %#x\n", get_state_name(ns->state), (uint)outb); ++ return outb; ++ } ++ ++ /* Status register may be read as many times as it is wanted */ ++ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { ++ NS_DBG("read_byte: return %#x status\n", ns->regs.status); ++ return ns->regs.status; ++ } ++ ++ /* Check if there is any data in the internal buffer which may be read */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); ++ return outb; ++ } ++ ++ switch (NS_STATE(ns->state)) { ++ case STATE_DATAOUT: ++ if (ns->busw == 8) { ++ outb = ns->buf.byte[ns->regs.count]; ++ ns->regs.count += 1; ++ } else { ++ outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); ++ ns->regs.count += 2; ++ } ++ break; ++ case STATE_DATAOUT_ID: ++ NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); ++ outb = ns->ids[ns->regs.count]; ++ ns->regs.count += 1; ++ break; ++ default: ++ BUG(); ++ } ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("read_byte: all bytes were read\n"); ++ ++ /* ++ * The OPT_AUTOINCR allows to read next conseqitive pages without ++ * new read operation cycle. ++ */ ++ if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { ++ ns->regs.count = 0; ++ if (ns->regs.row + 1 < ns->geom.pgnum) ++ ns->regs.row += 1; ++ NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); ++ do_state_action(ns, ACTION_CPY); ++ } ++ else if (NS_STATE(ns->nxstate) == STATE_READY) ++ switch_state(ns); ++ ++ } ++ ++ return outb; ++} ++ ++static void ++ns_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("write_byte: chip is disabled, ignore write\n"); ++ return; ++ } ++ if (ns->lines.ale && ns->lines.cle) { ++ NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); ++ return; ++ } ++ ++ if (ns->lines.cle == 1) { ++ /* ++ * The byte written is a command. ++ */ ++ ++ if (byte == NAND_CMD_RESET) { ++ NS_LOG("reset chip\n"); ++ switch_to_ready_state(ns, NS_STATUS_OK(ns)); ++ return; ++ } ++ ++ /* ++ * Chip might still be in STATE_DATAOUT ++ * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or ++ * STATE_DATAOUT_STATUS_M state. If so, switch state. ++ */ ++ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS ++ || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M ++ || ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT)) ++ switch_state(ns); ++ ++ /* Check if chip is expecting command */ ++ if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { ++ /* ++ * We are in situation when something else (not command) ++ * was expected but command was input. In this case ignore ++ * previous command(s)/state(s) and accept the last one. ++ */ ++ NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " ++ "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ } ++ ++ /* Check that the command byte is correct */ ++ if (check_command(byte)) { ++ NS_ERR("write_byte: unknown command %#x\n", (uint)byte); ++ return; ++ } ++ ++ NS_DBG("command byte corresponding to %s state accepted\n", ++ get_state_name(get_state_by_command(byte))); ++ ns->regs.command = byte; ++ switch_state(ns); ++ ++ } else if (ns->lines.ale == 1) { ++ /* ++ * The byte written is an address. ++ */ ++ ++ if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { ++ ++ NS_DBG("write_byte: operation isn't known yet, identify it\n"); ++ ++ if (find_operation(ns, 1) < 0) ++ return; ++ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ ns->regs.count = 0; ++ switch (NS_STATE(ns->nxstate)) { ++ case STATE_ADDR_PAGE: ++ ns->regs.num = ns->geom.pgaddrbytes; ++ break; ++ case STATE_ADDR_SEC: ++ ns->regs.num = ns->geom.secaddrbytes; ++ break; ++ case STATE_ADDR_ZERO: ++ ns->regs.num = 1; ++ break; ++ default: ++ BUG(); ++ } ++ } ++ ++ /* Check that chip is expecting address */ ++ if (!(ns->nxstate & STATE_ADDR_MASK)) { ++ NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " ++ "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if this is expected byte */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_ERR("write_byte: no more address bytes expected\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ accept_addr_byte(ns, byte); ++ ++ ns->regs.count += 1; ++ ++ NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", ++ (uint)byte, ns->regs.count, ns->regs.num); ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); ++ switch_state(ns); ++ } ++ ++ } else { ++ /* ++ * The byte written is an input data. ++ */ ++ ++ /* Check that chip is expecting data input */ ++ if (!(ns->state & STATE_DATAIN_MASK)) { ++ NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " ++ "switch to %s\n", (uint)byte, ++ get_state_name(ns->state), get_state_name(STATE_READY)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if this is expected byte */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", ++ ns->regs.num); ++ return; ++ } ++ ++ if (ns->busw == 8) { ++ ns->buf.byte[ns->regs.count] = byte; ++ ns->regs.count += 1; ++ } else { ++ ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); ++ ns->regs.count += 2; ++ } ++ } ++ ++ return; ++} ++ ++static int ++ns_device_ready(struct mtd_info *mtd) ++{ ++ NS_DBG("device_ready\n"); ++ return 1; ++} ++ ++static uint16_t ++ns_nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ NS_DBG("read_word\n"); ++ ++ return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); ++} ++ ++static void ++ns_nand_write_word(struct mtd_info *mtd, uint16_t word) ++{ ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ NS_DBG("write_word\n"); ++ ++ chip->write_byte(mtd, word & 0xFF); ++ chip->write_byte(mtd, word >> 8); ++} ++ ++static void ++ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; ++ ++ /* Check that chip is expecting data input */ ++ if (!(ns->state & STATE_DATAIN_MASK)) { ++ NS_ERR("write_buf: data input isn't expected, state is %s, " ++ "switch to STATE_READY\n", get_state_name(ns->state)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if these are expected bytes */ ++ if (ns->regs.count + len > ns->regs.num) { ++ NS_ERR("write_buf: too many input bytes\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ memcpy(ns->buf.byte + ns->regs.count, buf, len); ++ ns->regs.count += len; ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); ++ } ++} ++ ++static void ++ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("read_buf: chip is disabled\n"); ++ return; ++ } ++ if (ns->lines.ale || ns->lines.cle) { ++ NS_ERR("read_buf: ALE or CLE pin is high\n"); ++ return; ++ } ++ if (!(ns->state & STATE_DATAOUT_MASK)) { ++ NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", ++ get_state_name(ns->state)); ++ return; ++ } ++ ++ if (NS_STATE(ns->state) != STATE_DATAOUT) { ++ int i; ++ ++ for (i = 0; i < len; i++) ++ buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd); ++ ++ return; ++ } ++ ++ /* Check if these are expected bytes */ ++ if (ns->regs.count + len > ns->regs.num) { ++ NS_ERR("read_buf: too many bytes to read\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ memcpy(buf, ns->buf.byte + ns->regs.count, len); ++ ns->regs.count += len; ++ ++ if (ns->regs.count == ns->regs.num) { ++ if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { ++ ns->regs.count = 0; ++ if (ns->regs.row + 1 < ns->geom.pgnum) ++ ns->regs.row += 1; ++ NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); ++ do_state_action(ns, ACTION_CPY); ++ } ++ else if (NS_STATE(ns->nxstate) == STATE_READY) ++ switch_state(ns); ++ } ++ ++ return; ++} ++ ++static int ++ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); ++ ++ if (!memcmp(buf, &ns_verify_buf[0], len)) { ++ NS_DBG("verify_buf: the buffer is OK\n"); ++ return 0; ++ } else { ++ NS_DBG("verify_buf: the buffer is wrong\n"); ++ return -EFAULT; ++ } ++} ++ ++/* ++ * Having only NAND chip IDs we call nand_scan which detects NAND flash ++ * parameters and then calls scan_bbt in order to scan/find/build the ++ * NAND flash bad block table. But since at that moment the NAND flash ++ * image isn't allocated in the simulator, errors arise. To avoid this ++ * we redefine the scan_bbt callback and initialize the nandsim structure ++ * before the flash media scanning. ++ */ ++int ns_scan_bbt(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ struct nandsim *ns = (struct nandsim *)(chip->priv); ++ int retval; ++ ++ if (!NS_IS_INITIALIZED(ns)) ++ if ((retval = init_nandsim(mtd)) != 0) { ++ NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); ++ return retval; ++ } ++ if ((retval = nand_default_bbt(mtd)) != 0) { ++ free_nandsim(ns); ++ return retval; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Module initialization function ++ */ ++int __init ns_init_module(void) ++{ ++ struct nand_chip *chip; ++ struct nandsim *nand; ++ int retval = -ENOMEM; ++ ++ if (bus_width != 8 && bus_width != 16) { ++ NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); ++ return -EINVAL; ++ } ++ ++ /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ ++ nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) ++ + sizeof(struct nandsim), GFP_KERNEL); ++ if (!nsmtd) { ++ NS_ERR("unable to allocate core structures.\n"); ++ return -ENOMEM; ++ } ++ memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) + ++ sizeof(struct nandsim)); ++ chip = (struct nand_chip *)(nsmtd + 1); ++ nsmtd->priv = (void *)chip; ++ nand = (struct nandsim *)(chip + 1); ++ chip->priv = (void *)nand; ++ ++ /* ++ * Register simulator's callbacks. ++ */ ++ chip->hwcontrol = ns_hwcontrol; ++ chip->read_byte = ns_nand_read_byte; ++ chip->dev_ready = ns_device_ready; ++ chip->scan_bbt = ns_scan_bbt; ++ chip->write_byte = ns_nand_write_byte; ++ chip->write_buf = ns_nand_write_buf; ++ chip->read_buf = ns_nand_read_buf; ++ chip->verify_buf = ns_nand_verify_buf; ++ chip->write_word = ns_nand_write_word; ++ chip->read_word = ns_nand_read_word; ++ chip->eccmode = NAND_ECC_SOFT; ++ ++ /* ++ * Perform minimum nandsim structure initialization to handle ++ * the initial ID read command correctly ++ */ ++ if (third_id_byte != 0xFF || fourth_id_byte != 0xFF) ++ nand->geom.idbytes = 4; ++ else ++ nand->geom.idbytes = 2; ++ nand->regs.status = NS_STATUS_OK(nand); ++ nand->nxstate = STATE_UNKNOWN; ++ nand->options |= OPT_PAGE256; /* temporary value */ ++ nand->ids[0] = first_id_byte; ++ nand->ids[1] = second_id_byte; ++ nand->ids[2] = third_id_byte; ++ nand->ids[3] = fourth_id_byte; ++ if (bus_width == 16) { ++ nand->busw = 16; ++ chip->options |= NAND_BUSWIDTH_16; ++ } ++ ++ if ((retval = nand_scan(nsmtd, 1)) != 0) { ++ NS_ERR("can't register NAND Simulator\n"); ++ if (retval > 0) ++ retval = -ENXIO; ++ goto error; ++ } ++ ++ /* Register NAND as one big partition */ ++ add_mtd_partitions(nsmtd, &nand->part, 1); ++ ++ return 0; ++ ++error: ++ kfree(nsmtd); ++ ++ return retval; ++} ++ ++module_init(ns_init_module); ++ ++/* ++ * Module clean-up function ++ */ ++static void __exit ns_cleanup_module(void) ++{ ++ struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); ++ ++ free_nandsim(ns); /* Free nandsim private resources */ ++ nand_release(nsmtd); /* Unregisterd drived */ ++ kfree(nsmtd); /* Free other structures */ ++} ++ ++module_exit(ns_cleanup_module); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Artem B. Bityuckiy"); ++MODULE_DESCRIPTION ("The NAND flash simulator"); ++ +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/ppchameleonevb.c +@@ -0,0 +1,420 @@ ++/* ++ * drivers/mtd/nand/ppchameleonevb.c ++ * ++ * Copyright (C) 2003 DAVE Srl (info@wawnet.biz) ++ * ++ * Derived from drivers/mtd/nand/edb7312.c ++ * ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash devices found on the ++ * PPChameleon/PPChameleonEVB system. ++ * PPChameleon options (autodetected): ++ * - BA model: no NAND ++ * - ME model: 32MB (Samsung K9F5608U0B) ++ * - HI model: 128MB (Samsung K9F1G08UOM) ++ * PPChameleonEVB options: ++ * - 32MB (Samsung K9F5608U0B) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#undef USE_READY_BUSY_PIN ++#define USE_READY_BUSY_PIN ++/* see datasheets (tR) */ ++#define NAND_BIG_DELAY_US 25 ++#define NAND_SMALL_DELAY_US 10 ++ ++/* handy sizes */ ++#define SZ_4M 0x00400000 ++#define NAND_SMALL_SIZE 0x02000000 ++#define NAND_MTD_NAME "ppchameleon-nand" ++#define NAND_EVB_MTD_NAME "ppchameleonevb-nand" ++ ++/* GPIO pins used to drive NAND chip mounted on processor module */ ++#define NAND_nCE_GPIO_PIN (0x80000000 >> 1) ++#define NAND_CLE_GPIO_PIN (0x80000000 >> 2) ++#define NAND_ALE_GPIO_PIN (0x80000000 >> 3) ++#define NAND_RB_GPIO_PIN (0x80000000 >> 4) ++/* GPIO pins used to drive NAND chip mounted on EVB */ ++#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14) ++#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15) ++#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16) ++#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31) ++ ++/* ++ * MTD structure for PPChameleonEVB board ++ */ ++static struct mtd_info *ppchameleon_mtd = NULL; ++static struct mtd_info *ppchameleonevb_mtd = NULL; ++ ++/* ++ * Module stuff ++ */ ++static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR; ++static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR; ++ ++#ifdef MODULE ++module_param(ppchameleon_fio_pbase, ulong, 0); ++module_param(ppchameleonevb_fio_pbase, ulong, 0); ++#else ++__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase); ++__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase); ++#endif ++ ++#ifdef CONFIG_MTD_PARTITIONS ++/* ++ * Define static partitions for flash devices ++ */ ++static struct mtd_partition partition_info_hi[] = { ++ { name: "PPChameleon HI Nand Flash", ++ offset: 0, ++ size: 128*1024*1024 } ++}; ++ ++static struct mtd_partition partition_info_me[] = { ++ { name: "PPChameleon ME Nand Flash", ++ offset: 0, ++ size: 32*1024*1024 } ++}; ++ ++static struct mtd_partition partition_info_evb[] = { ++ { name: "PPChameleonEVB Nand Flash", ++ offset: 0, ++ size: 32*1024*1024 } ++}; ++ ++#define NUM_PARTITIONS 1 ++ ++extern int parse_cmdline_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, ++ const char *mtd_id); ++#endif ++ ++ ++/* ++ * hardware specific access to control-lines ++ */ ++static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd) ++{ ++ switch(cmd) { ++ ++ case NAND_CTL_SETCLE: ++ MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ case NAND_CTL_CLRCLE: ++ MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ case NAND_CTL_SETALE: ++ MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ case NAND_CTL_CLRALE: ++ MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ case NAND_CTL_SETNCE: ++ MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ case NAND_CTL_CLRNCE: ++ MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR); ++ break; ++ } ++} ++ ++static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd) ++{ ++ switch(cmd) { ++ ++ case NAND_CTL_SETCLE: ++ MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ case NAND_CTL_CLRCLE: ++ MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ case NAND_CTL_SETALE: ++ MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ case NAND_CTL_CLRALE: ++ MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ case NAND_CTL_SETNCE: ++ MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ case NAND_CTL_CLRNCE: ++ MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR); ++ break; ++ } ++} ++ ++#ifdef USE_READY_BUSY_PIN ++/* ++ * read device ready pin ++ */ ++static int ppchameleon_device_ready(struct mtd_info *minfo) ++{ ++ if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN) ++ return 1; ++ return 0; ++} ++ ++static int ppchameleonevb_device_ready(struct mtd_info *minfo) ++{ ++ if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN) ++ return 1; ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_MTD_PARTITIONS ++const char *part_probes[] = { "cmdlinepart", NULL }; ++const char *part_probes_evb[] = { "cmdlinepart", NULL }; ++#endif ++ ++/* ++ * Main initialization routine ++ */ ++static int __init ppchameleonevb_init (void) ++{ ++ struct nand_chip *this; ++ const char *part_type = 0; ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ void __iomem *ppchameleon_fio_base; ++ void __iomem *ppchameleonevb_fio_base; ++ ++ ++ /********************************* ++ * Processor module NAND (if any) * ++ *********************************/ ++ /* Allocate memory for MTD device structure and private data */ ++ ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + ++ sizeof(struct nand_chip), GFP_KERNEL); ++ if (!ppchameleon_mtd) { ++ printk("Unable to allocate PPChameleon NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* map physical address */ ++ ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M); ++ if(!ppchameleon_fio_base) { ++ printk("ioremap PPChameleon NAND flash failed\n"); ++ kfree(ppchameleon_mtd); ++ return -EIO; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&ppchameleon_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ ppchameleon_mtd->priv = this; ++ ++ /* Initialize GPIOs */ ++ /* Pin mapping for NAND chip */ ++ /* ++ CE GPIO_01 ++ CLE GPIO_02 ++ ALE GPIO_03 ++ R/B GPIO_04 ++ */ ++ /* output select */ ++ out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF); ++ /* three-state select */ ++ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF); ++ /* enable output driver */ ++ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN); ++#ifdef USE_READY_BUSY_PIN ++ /* three-state select */ ++ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF); ++ /* high-impedecence */ ++ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN)); ++ /* input select */ ++ out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000); ++#endif ++ ++ /* insert callbacks */ ++ this->IO_ADDR_R = ppchameleon_fio_base; ++ this->IO_ADDR_W = ppchameleon_fio_base; ++ this->hwcontrol = ppchameleon_hwcontrol; ++#ifdef USE_READY_BUSY_PIN ++ this->dev_ready = ppchameleon_device_ready; ++#endif ++ this->chip_delay = NAND_BIG_DELAY_US; ++ /* ECC mode */ ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existence of the device (it could not be mounted) */ ++ if (nand_scan (ppchameleon_mtd, 1)) { ++ iounmap((void *)ppchameleon_fio_base); ++ kfree (ppchameleon_mtd); ++ goto nand_evb_init; ++ } ++ ++#ifndef USE_READY_BUSY_PIN ++ /* Adjust delay if necessary */ ++ if (ppchameleon_mtd->size == NAND_SMALL_SIZE) ++ this->chip_delay = NAND_SMALL_DELAY_US; ++#endif ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ppchameleon_mtd->name = "ppchameleon-nand"; ++ mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0); ++ if (mtd_parts_nb > 0) ++ part_type = "command line"; ++ else ++ mtd_parts_nb = 0; ++#endif ++ if (mtd_parts_nb == 0) ++ { ++ if (ppchameleon_mtd->size == NAND_SMALL_SIZE) ++ mtd_parts = partition_info_me; ++ else ++ mtd_parts = partition_info_hi; ++ mtd_parts_nb = NUM_PARTITIONS; ++ part_type = "static"; ++ } ++ ++ /* Register the partitions */ ++ printk(KERN_NOTICE "Using %s partition definition\n", part_type); ++ add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb); ++ ++nand_evb_init: ++ /**************************** ++ * EVB NAND (always present) * ++ ****************************/ ++ /* Allocate memory for MTD device structure and private data */ ++ ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + ++ sizeof(struct nand_chip), GFP_KERNEL); ++ if (!ppchameleonevb_mtd) { ++ printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* map physical address */ ++ ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M); ++ if(!ppchameleonevb_fio_base) { ++ printk("ioremap PPChameleonEVB NAND flash failed\n"); ++ kfree(ppchameleonevb_mtd); ++ return -EIO; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&ppchameleonevb_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ ppchameleonevb_mtd->priv = this; ++ ++ /* Initialize GPIOs */ ++ /* Pin mapping for NAND chip */ ++ /* ++ CE GPIO_14 ++ CLE GPIO_15 ++ ALE GPIO_16 ++ R/B GPIO_31 ++ */ ++ /* output select */ ++ out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0); ++ out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF); ++ /* three-state select */ ++ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0); ++ out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF); ++ /* enable output driver */ ++ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | ++ NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN); ++#ifdef USE_READY_BUSY_PIN ++ /* three-state select */ ++ out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC); ++ /* high-impedecence */ ++ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN)); ++ /* input select */ ++ out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001); ++#endif ++ ++ /* insert callbacks */ ++ this->IO_ADDR_R = ppchameleonevb_fio_base; ++ this->IO_ADDR_W = ppchameleonevb_fio_base; ++ this->hwcontrol = ppchameleonevb_hwcontrol; ++#ifdef USE_READY_BUSY_PIN ++ this->dev_ready = ppchameleonevb_device_ready; ++#endif ++ this->chip_delay = NAND_SMALL_DELAY_US; ++ ++ /* ECC mode */ ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (ppchameleonevb_mtd, 1)) { ++ iounmap((void *)ppchameleonevb_fio_base); ++ kfree (ppchameleonevb_mtd); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; ++ mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0); ++ if (mtd_parts_nb > 0) ++ part_type = "command line"; ++ else ++ mtd_parts_nb = 0; ++#endif ++ if (mtd_parts_nb == 0) ++ { ++ mtd_parts = partition_info_evb; ++ mtd_parts_nb = NUM_PARTITIONS; ++ part_type = "static"; ++ } ++ ++ /* Register the partitions */ ++ printk(KERN_NOTICE "Using %s partition definition\n", part_type); ++ add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb); ++ ++ /* Return happy */ ++ return 0; ++} ++module_init(ppchameleonevb_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit ppchameleonevb_cleanup (void) ++{ ++ struct nand_chip *this; ++ ++ /* Release resources, unregister device(s) */ ++ nand_release (ppchameleon_mtd); ++ nand_release (ppchameleonevb_mtd); ++ ++ /* Release iomaps */ ++ this = (struct nand_chip *) &ppchameleon_mtd[1]; ++ iounmap((void *) this->IO_ADDR_R; ++ this = (struct nand_chip *) &ppchameleonevb_mtd[1]; ++ iounmap((void *) this->IO_ADDR_R; ++ ++ /* Free the MTD device structure */ ++ kfree (ppchameleon_mtd); ++ kfree (ppchameleonevb_mtd); ++} ++module_exit(ppchameleonevb_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("DAVE Srl "); ++MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/rtc_from4.c +@@ -0,0 +1,683 @@ ++/* ++ * drivers/mtd/nand/rtc_from4.c ++ * ++ * Copyright (C) 2004 Red Hat, Inc. ++ * ++ * Derived from drivers/mtd/nand/spia.c ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the AG-AND flash device found on the ++ * Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), ++ * which utilizes the Renesas HN29V1G91T-30 part. ++ * This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * MTD structure for Renesas board ++ */ ++static struct mtd_info *rtc_from4_mtd = NULL; ++ ++#define RTC_FROM4_MAX_CHIPS 2 ++ ++/* HS77x9 processor register defines */ ++#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60)) ++#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62)) ++#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64)) ++#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66)) ++#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68)) ++#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C)) ++#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80)) ++ ++/* ++ * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor) ++ */ ++/* Address where flash is mapped */ ++#define RTC_FROM4_FIO_BASE 0x14000000 ++ ++/* CLE and ALE are tied to address lines 5 & 4, respectively */ ++#define RTC_FROM4_CLE (1 << 5) ++#define RTC_FROM4_ALE (1 << 4) ++ ++/* address lines A24-A22 used for chip selection */ ++#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000) ++#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000) ++#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000) ++/* mask address lines A24-A22 used for chip selection */ ++#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA) ++ ++/* FPGA status register for checking device ready (bit zero) */ ++#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002) ++#define RTC_FROM4_DEVICE_READY 0x0001 ++ ++/* FPGA Reed-Solomon ECC Control register */ ++ ++#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050) ++#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7) ++#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6) ++#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5) ++ ++/* FPGA Reed-Solomon ECC code base */ ++#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060) ++#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080) ++ ++/* FPGA Reed-Solomon ECC check register */ ++#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) ++#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) ++ ++#define ERR_STAT_ECC_AVAILABLE 0x20 ++ ++/* Undefine for software ECC */ ++#define RTC_FROM4_HWECC 1 ++ ++/* Define as 1 for no virtual erase blocks (in JFFS2) */ ++#define RTC_FROM4_NO_VIRTBLOCKS 0 ++ ++/* ++ * Module stuff ++ */ ++static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); ++ ++const static struct mtd_partition partition_info[] = { ++ { ++ .name = "Renesas flash partition 1", ++ .offset = 0, ++ .size = MTDPART_SIZ_FULL ++ }, ++}; ++#define NUM_PARTITIONS 1 ++ ++/* ++ * hardware specific flash bbt decriptors ++ * Note: this is to allow debugging by disabling ++ * NAND_BBT_CREATE and/or NAND_BBT_WRITE ++ * ++ */ ++static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; ++static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr rtc_from4_bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 40, ++ .len = 4, ++ .veroffs = 44, ++ .maxblocks = 4, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 40, ++ .len = 4, ++ .veroffs = 44, ++ .maxblocks = 4, ++ .pattern = mirror_pattern ++}; ++ ++ ++ ++#ifdef RTC_FROM4_HWECC ++ ++/* the Reed Solomon control structure */ ++static struct rs_control *rs_decoder; ++ ++/* ++ * hardware specific Out Of Band information ++ */ ++static struct nand_oobinfo rtc_from4_nand_oobinfo = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 32, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, ++ 8, 9, 10, 11, 12, 13, 14, 15, ++ 16, 17, 18, 19, 20, 21, 22, 23, ++ 24, 25, 26, 27, 28, 29, 30, 31}, ++ .oobfree = { {32, 32} } ++}; ++ ++/* Aargh. I missed the reversed bit order, when I ++ * was talking to Renesas about the FPGA. ++ * ++ * The table is used for bit reordering and inversion ++ * of the ecc byte which we get from the FPGA ++ */ ++static uint8_t revbits[256] = { ++ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, ++ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, ++ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, ++ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, ++ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, ++ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, ++ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, ++ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, ++ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, ++ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, ++ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, ++ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, ++ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, ++ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, ++ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, ++ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, ++ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, ++ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, ++ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, ++ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, ++ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, ++ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, ++ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, ++ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, ++ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, ++ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, ++ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, ++ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, ++ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, ++ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, ++ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, ++ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, ++}; ++ ++#endif ++ ++ ++ ++/* ++ * rtc_from4_hwcontrol - hardware specific access to control-lines ++ * @mtd: MTD device structure ++ * @cmd: hardware control command ++ * ++ * Address lines (A5 and A4) are used to control Command and Address Latch ++ * Enable on this board, so set the read/write address appropriately. ++ * ++ * Chip Enable is also controlled by the Chip Select (CS5) and ++ * Address lines (A24-A22), so no action is required here. ++ * ++ */ ++static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip* this = (struct nand_chip *) (mtd->priv); ++ ++ switch(cmd) { ++ ++ case NAND_CTL_SETCLE: ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE); ++ break; ++ case NAND_CTL_CLRCLE: ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE); ++ break; ++ ++ case NAND_CTL_SETALE: ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE); ++ break; ++ case NAND_CTL_CLRALE: ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE); ++ break; ++ ++ case NAND_CTL_SETNCE: ++ break; ++ case NAND_CTL_CLRNCE: ++ break; ++ ++ } ++} ++ ++ ++/* ++ * rtc_from4_nand_select_chip - hardware specific chip select ++ * @mtd: MTD device structure ++ * @chip: Chip to select (0 == slot 3, 1 == slot 4) ++ * ++ * The chip select is based on address lines A24-A22. ++ * This driver uses flash slots 3 and 4 (A23-A22). ++ * ++ */ ++static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK); ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK); ++ ++ switch(chip) { ++ ++ case 0: /* select slot 3 chip */ ++ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3); ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3); ++ break; ++ case 1: /* select slot 4 chip */ ++ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4); ++ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4); ++ break; ++ ++ } ++} ++ ++ ++/* ++ * rtc_from4_nand_device_ready - hardware specific ready/busy check ++ * @mtd: MTD device structure ++ * ++ * This board provides the Ready/Busy state in the status register ++ * of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal. ++ * ++ */ ++static int rtc_from4_nand_device_ready(struct mtd_info *mtd) ++{ ++ unsigned short status; ++ ++ status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR)); ++ ++ return (status & RTC_FROM4_DEVICE_READY); ++ ++} ++ ++ ++/* ++ * deplete - code to perform device recovery in case there was a power loss ++ * @mtd: MTD device structure ++ * @chip: Chip to select (0 == slot 3, 1 == slot 4) ++ * ++ * If there was a sudden loss of power during an erase operation, a ++ * "device recovery" operation must be performed when power is restored ++ * to ensure correct operation. This routine performs the required steps ++ * for the requested chip. ++ * ++ * See page 86 of the data sheet for details. ++ * ++ */ ++static void deplete(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ /* wait until device is ready */ ++ while (!this->dev_ready(mtd)); ++ ++ this->select_chip(mtd, chip); ++ ++ /* Send the commands for device recovery, phase 1 */ ++ this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); ++ this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); ++ ++ /* Send the commands for device recovery, phase 2 */ ++ this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); ++ this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); ++ ++} ++ ++ ++#ifdef RTC_FROM4_HWECC ++/* ++ * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function ++ * @mtd: MTD device structure ++ * @mode: I/O mode; read or write ++ * ++ * enable hardware ECC for data read or write ++ * ++ */ ++static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL); ++ unsigned short status; ++ ++ switch (mode) { ++ case NAND_ECC_READ : ++ status = RTC_FROM4_RS_ECC_CTL_CLR ++ | RTC_FROM4_RS_ECC_CTL_FD_E; ++ ++ *rs_ecc_ctl = status; ++ break; ++ ++ case NAND_ECC_READSYN : ++ status = 0x00; ++ ++ *rs_ecc_ctl = status; ++ break; ++ ++ case NAND_ECC_WRITE : ++ status = RTC_FROM4_RS_ECC_CTL_CLR ++ | RTC_FROM4_RS_ECC_CTL_GEN ++ | RTC_FROM4_RS_ECC_CTL_FD_E; ++ ++ *rs_ecc_ctl = status; ++ break; ++ ++ default: ++ BUG(); ++ break; ++ } ++ ++} ++ ++ ++/* ++ * rtc_from4_calculate_ecc - hardware specific code to read ECC code ++ * @mtd: MTD device structure ++ * @dat: buffer containing the data to generate ECC codes ++ * @ecc_code ECC codes calculated ++ * ++ * The ECC code is calculated by the FPGA. All we have to do is read the values ++ * from the FPGA registers. ++ * ++ * Note: We read from the inverted registers, since data is inverted before ++ * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code ++ * ++ */ ++static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) ++{ ++ volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN); ++ unsigned short value; ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ value = *rs_eccn; ++ ecc_code[i] = (unsigned char)value; ++ rs_eccn++; ++ } ++ ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ ++} ++ ++ ++/* ++ * rtc_from4_correct_data - hardware specific code to correct data using ECC code ++ * @mtd: MTD device structure ++ * @buf: buffer containing the data to generate ECC codes ++ * @ecc1 ECC codes read ++ * @ecc2 ECC codes calculated ++ * ++ * The FPGA tells us fast, if there's an error or not. If no, we go back happy ++ * else we read the ecc results from the fpga and call the rs library to decode ++ * and hopefully correct the error. ++ * ++ */ ++static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) ++{ ++ int i, j, res; ++ unsigned short status; ++ uint16_t par[6], syn[6]; ++ uint8_t ecc[8]; ++ volatile unsigned short *rs_ecc; ++ ++ status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK)); ++ ++ if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) { ++ return 0; ++ } ++ ++ /* Read the syndrom pattern from the FPGA and correct the bitorder */ ++ rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); ++ for (i = 0; i < 8; i++) { ++ ecc[i] = revbits[(*rs_ecc) & 0xFF]; ++ rs_ecc++; ++ } ++ ++ /* convert into 6 10bit syndrome fields */ ++ par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) | ++ (((uint16_t)ecc[1] << 8) & 0x300)]; ++ par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) | ++ (((uint16_t)ecc[2] << 6) & 0x3c0)]; ++ par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) | ++ (((uint16_t)ecc[3] << 4) & 0x3f0)]; ++ par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) | ++ (((uint16_t)ecc[4] << 2) & 0x3fc)]; ++ par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) | ++ (((uint16_t)ecc[6] << 8) & 0x300)]; ++ par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0); ++ ++ /* Convert to computable syndrome */ ++ for (i = 0; i < 6; i++) { ++ syn[i] = par[0]; ++ for (j = 1; j < 6; j++) ++ if (par[j] != rs_decoder->nn) ++ syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)]; ++ ++ /* Convert to index form */ ++ syn[i] = rs_decoder->index_of[syn[i]]; ++ } ++ ++ /* Let the library code do its magic.*/ ++ res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL); ++ if (res > 0) { ++ DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " ++ "ECC corrected %d errors on read\n", res); ++ } ++ return res; ++} ++ ++ ++/** ++ * rtc_from4_errstat - perform additional error status checks ++ * @mtd: MTD device structure ++ * @this: NAND chip structure ++ * @state: state or the operation ++ * @status: status code returned from read status ++ * @page: startpage inside the chip, must be called with (page & this->pagemask) ++ * ++ * Perform additional error status checks on erase and write failures ++ * to determine if errors are correctable. For this device, correctable ++ * 1-bit errors on erase and write are considered acceptable. ++ * ++ * note: see pages 34..37 of data sheet for details. ++ * ++ */ ++static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) ++{ ++ int er_stat=0; ++ int rtn, retlen; ++ size_t len; ++ uint8_t *buf; ++ int i; ++ ++ this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1); ++ ++ if (state == FL_ERASING) { ++ for (i=0; i<4; i++) { ++ if (status & 1<<(i+1)) { ++ this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); ++ rtn = this->read_byte(mtd); ++ this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); ++ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { ++ er_stat |= 1<<(i+1); /* err_ecc_not_avail */ ++ } ++ } ++ } ++ } else if (state == FL_WRITING) { ++ /* single bank write logic */ ++ this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1); ++ rtn = this->read_byte(mtd); ++ this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); ++ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { ++ er_stat |= 1<<1; /* err_ecc_not_avail */ ++ } else { ++ len = mtd->oobblock; ++ buf = kmalloc (len, GFP_KERNEL); ++ if (!buf) { ++ printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n"); ++ er_stat = 1; /* if we can't check, assume failed */ ++ } else { ++ /* recovery read */ ++ /* page read */ ++ rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1); ++ if (rtn) { /* if read failed or > 1-bit error corrected */ ++ er_stat |= 1<<1; /* ECC read failed */ ++ } ++ kfree(buf); ++ } ++ } ++ } ++ ++ rtn = status; ++ if (er_stat == 0) { /* if ECC is available */ ++ rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ ++ } ++ ++ return rtn; ++} ++#endif ++ ++ ++/* ++ * Main initialization routine ++ */ ++int __init rtc_from4_init (void) ++{ ++ struct nand_chip *this; ++ unsigned short bcr1, bcr2, wcr2; ++ int i; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!rtc_from4_mtd) { ++ printk ("Unable to allocate Renesas NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&rtc_from4_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ rtc_from4_mtd->priv = this; ++ ++ /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */ ++ bcr1 = *SH77X9_BCR1 & ~0x0002; ++ bcr1 |= 0x0002; ++ *SH77X9_BCR1 = bcr1; ++ ++ /* set */ ++ bcr2 = *SH77X9_BCR2 & ~0x0c00; ++ bcr2 |= 0x0800; ++ *SH77X9_BCR2 = bcr2; ++ ++ /* set area 5 wait states */ ++ wcr2 = *SH77X9_WCR2 & ~0x1c00; ++ wcr2 |= 0x1c00; ++ *SH77X9_WCR2 = wcr2; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = rtc_from4_fio_base; ++ this->IO_ADDR_W = rtc_from4_fio_base; ++ /* Set address of hardware control function */ ++ this->hwcontrol = rtc_from4_hwcontrol; ++ /* Set address of chip select function */ ++ this->select_chip = rtc_from4_nand_select_chip; ++ /* command delay time (in us) */ ++ this->chip_delay = 100; ++ /* return the status of the Ready/Busy line */ ++ this->dev_ready = rtc_from4_nand_device_ready; ++ ++#ifdef RTC_FROM4_HWECC ++ printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n"); ++ ++ this->eccmode = NAND_ECC_HW8_512; ++ this->options |= NAND_HWECC_SYNDROME; ++ /* return the status of extra status and ECC checks */ ++ this->errstat = rtc_from4_errstat; ++ /* set the nand_oobinfo to support FPGA H/W error detection */ ++ this->autooob = &rtc_from4_nand_oobinfo; ++ this->enable_hwecc = rtc_from4_enable_hwecc; ++ this->calculate_ecc = rtc_from4_calculate_ecc; ++ this->correct_data = rtc_from4_correct_data; ++#else ++ printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); ++ ++ this->eccmode = NAND_ECC_SOFT; ++#endif ++ ++ /* set the bad block tables to support debugging */ ++ this->bbt_td = &rtc_from4_bbt_main_descr; ++ this->bbt_md = &rtc_from4_bbt_mirror_descr; ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { ++ kfree(rtc_from4_mtd); ++ return -ENXIO; ++ } ++ ++ /* Perform 'device recovery' for each chip in case there was a power loss. */ ++ for (i=0; i < this->numchips; i++) { ++ deplete(rtc_from4_mtd, i); ++ } ++ ++#if RTC_FROM4_NO_VIRTBLOCKS ++ /* use a smaller erase block to minimize wasted space when a block is bad */ ++ /* note: this uses eight times as much RAM as using the default and makes */ ++ /* mounts take four times as long. */ ++ rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; ++#endif ++ ++ /* Register the partitions */ ++ add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); ++ ++#ifdef RTC_FROM4_HWECC ++ /* We could create the decoder on demand, if memory is a concern. ++ * This way we have it handy, if an error happens ++ * ++ * Symbolsize is 10 (bits) ++ * Primitve polynomial is x^10+x^3+1 ++ * first consecutive root is 0 ++ * primitve element to generate roots = 1 ++ * generator polinomial degree = 6 ++ */ ++ rs_decoder = init_rs(10, 0x409, 0, 1, 6); ++ if (!rs_decoder) { ++ printk (KERN_ERR "Could not create a RS decoder\n"); ++ nand_release(rtc_from4_mtd); ++ kfree(rtc_from4_mtd); ++ return -ENOMEM; ++ } ++#endif ++ /* Return happy */ ++ return 0; ++} ++module_init(rtc_from4_init); ++ ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit rtc_from4_cleanup (void) ++{ ++ /* Release resource, unregister partitions */ ++ nand_release(rtc_from4_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (rtc_from4_mtd); ++ ++#ifdef RTC_FROM4_HWECC ++ /* Free the reed solomon resources */ ++ if (rs_decoder) { ++ free_rs(rs_decoder); ++ } ++#endif ++} ++module_exit(rtc_from4_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("d.marlin ++ * ++ * Samsung S3C2410 NAND driver ++ * ++ * Changelog: ++ * 21-Sep-2004 BJD Initial version ++ * 23-Sep-2004 BJD Mulitple device support ++ * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode ++ * 12-Oct-2004 BJD Fixed errors in use of platform data ++ * 18-Feb-2004 BJD Fix sparse errors ++ * ++ * $Id$ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#include ++#include ++ ++#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG ++#define DEBUG ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define PFX "s3c2410-nand: " ++ ++#ifdef CONFIG_MTD_NAND_S3C2410_HWECC ++static int hardware_ecc = 1; ++#else ++static int hardware_ecc = 0; ++#endif ++ ++/* new oob placement block for use with hardware ecc generation ++ */ ++ ++static struct nand_oobinfo nand_hw_eccoob = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 3, ++ .eccpos = {0, 1, 2 }, ++ .oobfree = { {8, 8} } ++}; ++ ++/* controller and mtd information */ ++ ++struct s3c2410_nand_info; ++ ++struct s3c2410_nand_mtd { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ struct s3c2410_nand_set *set; ++ struct s3c2410_nand_info *info; ++ int scan_res; ++}; ++ ++/* overview of the s3c2410 nand state */ ++ ++struct s3c2410_nand_info { ++ /* mtd info */ ++ struct nand_hw_control controller; ++ struct s3c2410_nand_mtd *mtds; ++ struct s3c2410_platform_nand *platform; ++ ++ /* device info */ ++ struct device *device; ++ struct resource *area; ++ struct clk *clk; ++ void __iomem *regs; ++ int mtd_count; ++}; ++ ++/* conversion functions */ ++ ++static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) ++{ ++ return container_of(mtd, struct s3c2410_nand_mtd, mtd); ++} ++ ++static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) ++{ ++ return s3c2410_nand_mtd_toours(mtd)->info; ++} ++ ++static struct s3c2410_nand_info *to_nand_info(struct device *dev) ++{ ++ return dev_get_drvdata(dev); ++} ++ ++static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) ++{ ++ return dev->platform_data; ++} ++ ++/* timing calculations */ ++ ++#define NS_IN_KHZ 10000000 ++ ++static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) ++{ ++ int result; ++ ++ result = (wanted * NS_IN_KHZ) / clk; ++ result++; ++ ++ pr_debug("result %d from %ld, %d\n", result, clk, wanted); ++ ++ if (result > max) { ++ printk("%d ns is too big for current clock rate %ld\n", ++ wanted, clk); ++ return -1; ++ } ++ ++ if (result < 1) ++ result = 1; ++ ++ return result; ++} ++ ++#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ) ++ ++/* controller setup */ ++ ++static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, ++ struct device *dev) ++{ ++ struct s3c2410_platform_nand *plat = to_nand_plat(dev); ++ unsigned int tacls, twrph0, twrph1; ++ unsigned long clkrate = clk_get_rate(info->clk); ++ unsigned long cfg; ++ ++ /* calculate the timing information for the controller */ ++ ++ if (plat != NULL) { ++ tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); ++ twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); ++ twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); ++ } else { ++ /* default timings */ ++ tacls = 8; ++ twrph0 = 8; ++ twrph1 = 8; ++ } ++ ++ if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { ++ printk(KERN_ERR PFX "cannot get timings suitable for board\n"); ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n", ++ to_ns(tacls, clkrate), ++ to_ns(twrph0, clkrate), ++ to_ns(twrph1, clkrate)); ++ ++ cfg = S3C2410_NFCONF_EN; ++ cfg |= S3C2410_NFCONF_TACLS(tacls-1); ++ cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); ++ cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); ++ ++ pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); ++ ++ writel(cfg, info->regs + S3C2410_NFCONF); ++ return 0; ++} ++ ++/* select chip */ ++ ++static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct s3c2410_nand_info *info; ++ struct s3c2410_nand_mtd *nmtd; ++ struct nand_chip *this = mtd->priv; ++ unsigned long cur; ++ ++ nmtd = this->priv; ++ info = nmtd->info; ++ ++ cur = readl(info->regs + S3C2410_NFCONF); ++ ++ if (chip == -1) { ++ cur |= S3C2410_NFCONF_nFCE; ++ } else { ++ if (chip > nmtd->set->nr_chips) { ++ printk(KERN_ERR PFX "chip %d out of range\n", chip); ++ return; ++ } ++ ++ if (info->platform != NULL) { ++ if (info->platform->select_chip != NULL) ++ (info->platform->select_chip)(nmtd->set, chip); ++ } ++ ++ cur &= ~S3C2410_NFCONF_nFCE; ++ } ++ ++ writel(cur, info->regs + S3C2410_NFCONF); ++} ++ ++/* command and control functions */ ++ ++static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long cur; ++ ++ switch (cmd) { ++ case NAND_CTL_SETNCE: ++ cur = readl(info->regs + S3C2410_NFCONF); ++ cur &= ~S3C2410_NFCONF_nFCE; ++ writel(cur, info->regs + S3C2410_NFCONF); ++ break; ++ ++ case NAND_CTL_CLRNCE: ++ cur = readl(info->regs + S3C2410_NFCONF); ++ cur |= S3C2410_NFCONF_nFCE; ++ writel(cur, info->regs + S3C2410_NFCONF); ++ break; ++ ++ /* we don't need to implement these */ ++ case NAND_CTL_SETCLE: ++ case NAND_CTL_CLRCLE: ++ case NAND_CTL_SETALE: ++ case NAND_CTL_CLRALE: ++ pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd); ++ break; ++ } ++} ++ ++/* s3c2410_nand_command ++ * ++ * This function implements sending commands and the relevant address ++ * information to the chip, via the hardware controller. Since the ++ * S3C2410 generates the correct ALE/CLE signaling automatically, we ++ * do not need to use hwcontrol. ++*/ ++ ++static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ register struct nand_chip *this = mtd->priv; ++ ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ ++ writeb(readcmd, info->regs + S3C2410_NFCMD); ++ } ++ writeb(command, info->regs + S3C2410_NFCMD); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ ++ if (column != -1 || page_addr != -1) { ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ writeb(column, info->regs + S3C2410_NFADDR); ++ } ++ if (page_addr != -1) { ++ writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR); ++ writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR); ++ /* One more address cycle for higher density devices */ ++ if (this->chipsize & 0x0c000000) ++ writeb((unsigned char) ((page_addr >> 16) & 0x0f), ++ info->regs + S3C2410_NFADDR); ++ } ++ /* Latch in address */ ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ ++ udelay(this->chip_delay); ++ writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD); ++ ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay (100); ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++ ++/* s3c2410_nand_devready() ++ * ++ * returns 0 if the nand is busy, 1 if it is ready ++*/ ++ ++static int s3c2410_nand_devready(struct mtd_info *mtd) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; ++} ++ ++/* ECC handling functions */ ++ ++static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", ++ mtd, dat, read_ecc, calc_ecc); ++ ++ pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", ++ read_ecc[0], read_ecc[1], read_ecc[2], ++ calc_ecc[0], calc_ecc[1], calc_ecc[2]); ++ ++ if (read_ecc[0] == calc_ecc[0] && ++ read_ecc[1] == calc_ecc[1] && ++ read_ecc[2] == calc_ecc[2]) ++ return 0; ++ ++ /* we curently have no method for correcting the error */ ++ ++ return -1; ++} ++ ++static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ctrl; ++ ++ ctrl = readl(info->regs + S3C2410_NFCONF); ++ ctrl |= S3C2410_NFCONF_INITECC; ++ writel(ctrl, info->regs + S3C2410_NFCONF); ++} ++ ++static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_code) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); ++ ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ++ ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); ++ ++ pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ++ ecc_code[0], ecc_code[1], ecc_code[2]); ++ ++ return 0; ++} ++ ++ ++/* over-ride the standard functions for a little more speed? */ ++ ++static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ readsb(this->IO_ADDR_R, buf, len); ++} ++ ++static void s3c2410_nand_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ writesb(this->IO_ADDR_W, buf, len); ++} ++ ++/* device management functions */ ++ ++static int s3c2410_nand_remove(struct device *dev) ++{ ++ struct s3c2410_nand_info *info = to_nand_info(dev); ++ ++ dev_set_drvdata(dev, NULL); ++ ++ if (info == NULL) ++ return 0; ++ ++ /* first thing we need to do is release all our mtds ++ * and their partitions, then go through freeing the ++ * resources used ++ */ ++ ++ if (info->mtds != NULL) { ++ struct s3c2410_nand_mtd *ptr = info->mtds; ++ int mtdno; ++ ++ for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { ++ pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); ++ nand_release(&ptr->mtd); ++ } ++ ++ kfree(info->mtds); ++ } ++ ++ /* free the common resources */ ++ ++ if (info->clk != NULL && !IS_ERR(info->clk)) { ++ clk_disable(info->clk); ++ clk_unuse(info->clk); ++ clk_put(info->clk); ++ } ++ ++ if (info->regs != NULL) { ++ iounmap(info->regs); ++ info->regs = NULL; ++ } ++ ++ if (info->area != NULL) { ++ release_resource(info->area); ++ kfree(info->area); ++ info->area = NULL; ++ } ++ ++ kfree(info); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *mtd, ++ struct s3c2410_nand_set *set) ++{ ++ if (set == NULL) ++ return add_mtd_device(&mtd->mtd); ++ ++ if (set->nr_partitions > 0 && set->partitions != NULL) { ++ return add_mtd_partitions(&mtd->mtd, ++ set->partitions, ++ set->nr_partitions); ++ } ++ ++ return add_mtd_device(&mtd->mtd); ++} ++#else ++static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *mtd, ++ struct s3c2410_nand_set *set) ++{ ++ return add_mtd_device(&mtd->mtd); ++} ++#endif ++ ++/* s3c2410_nand_init_chip ++ * ++ * init a single instance of an chip ++*/ ++ ++static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *nmtd, ++ struct s3c2410_nand_set *set) ++{ ++ struct nand_chip *chip = &nmtd->chip; ++ ++ chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; ++ chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; ++ chip->hwcontrol = s3c2410_nand_hwcontrol; ++ chip->dev_ready = s3c2410_nand_devready; ++ chip->cmdfunc = s3c2410_nand_command; ++ chip->write_buf = s3c2410_nand_write_buf; ++ chip->read_buf = s3c2410_nand_read_buf; ++ chip->select_chip = s3c2410_nand_select_chip; ++ chip->chip_delay = 50; ++ chip->priv = nmtd; ++ chip->options = 0; ++ chip->controller = &info->controller; ++ ++ nmtd->info = info; ++ nmtd->mtd.priv = chip; ++ nmtd->set = set; ++ ++ if (hardware_ecc) { ++ chip->correct_data = s3c2410_nand_correct_data; ++ chip->enable_hwecc = s3c2410_nand_enable_hwecc; ++ chip->calculate_ecc = s3c2410_nand_calculate_ecc; ++ chip->eccmode = NAND_ECC_HW3_512; ++ chip->autooob = &nand_hw_eccoob; ++ } else { ++ chip->eccmode = NAND_ECC_SOFT; ++ } ++} ++ ++/* s3c2410_nand_probe ++ * ++ * called by device layer when it finds a device matching ++ * one our driver can handled. This code checks to see if ++ * it can allocate all necessary resources then calls the ++ * nand layer to look for devices ++*/ ++ ++static int s3c2410_nand_probe(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct s3c2410_platform_nand *plat = to_nand_plat(dev); ++ struct s3c2410_nand_info *info; ++ struct s3c2410_nand_mtd *nmtd; ++ struct s3c2410_nand_set *sets; ++ struct resource *res; ++ int err = 0; ++ int size; ++ int nr_sets; ++ int setno; ++ ++ pr_debug("s3c2410_nand_probe(%p)\n", dev); ++ ++ info = kmalloc(sizeof(*info), GFP_KERNEL); ++ if (info == NULL) { ++ printk(KERN_ERR PFX "no memory for flash info\n"); ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ memzero(info, sizeof(*info)); ++ dev_set_drvdata(dev, info); ++ ++ spin_lock_init(&info->controller.lock); ++ ++ /* get the clock source and enable it */ ++ ++ info->clk = clk_get(dev, "nand"); ++ if (IS_ERR(info->clk)) { ++ printk(KERN_ERR PFX "failed to get clock"); ++ err = -ENOENT; ++ goto exit_error; ++ } ++ ++ clk_use(info->clk); ++ clk_enable(info->clk); ++ ++ /* allocate and map the resource */ ++ ++ res = pdev->resource; /* assume that the flash has one resource */ ++ size = res->end - res->start + 1; ++ ++ info->area = request_mem_region(res->start, size, pdev->name); ++ ++ if (info->area == NULL) { ++ printk(KERN_ERR PFX "cannot reserve register region\n"); ++ err = -ENOENT; ++ goto exit_error; ++ } ++ ++ info->device = dev; ++ info->platform = plat; ++ info->regs = ioremap(res->start, size); ++ ++ if (info->regs == NULL) { ++ printk(KERN_ERR PFX "cannot reserve register region\n"); ++ err = -EIO; ++ goto exit_error; ++ } ++ ++ printk(KERN_INFO PFX "mapped registers at %p\n", info->regs); ++ ++ /* initialise the hardware */ ++ ++ err = s3c2410_nand_inithw(info, dev); ++ if (err != 0) ++ goto exit_error; ++ ++ sets = (plat != NULL) ? plat->sets : NULL; ++ nr_sets = (plat != NULL) ? plat->nr_sets : 1; ++ ++ info->mtd_count = nr_sets; ++ ++ /* allocate our information */ ++ ++ size = nr_sets * sizeof(*info->mtds); ++ info->mtds = kmalloc(size, GFP_KERNEL); ++ if (info->mtds == NULL) { ++ printk(KERN_ERR PFX "failed to allocate mtd storage\n"); ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ memzero(info->mtds, size); ++ ++ /* initialise all possible chips */ ++ ++ nmtd = info->mtds; ++ ++ for (setno = 0; setno < nr_sets; setno++, nmtd++) { ++ pr_debug("initialising set %d (%p, info %p)\n", ++ setno, nmtd, info); ++ ++ s3c2410_nand_init_chip(info, nmtd, sets); ++ ++ nmtd->scan_res = nand_scan(&nmtd->mtd, ++ (sets) ? sets->nr_chips : 1); ++ ++ if (nmtd->scan_res == 0) { ++ s3c2410_nand_add_partition(info, nmtd, sets); ++ } ++ ++ if (sets != NULL) ++ sets++; ++ } ++ ++ pr_debug("initialised ok\n"); ++ return 0; ++ ++ exit_error: ++ s3c2410_nand_remove(dev); ++ ++ if (err == 0) ++ err = -EINVAL; ++ return err; ++} ++ ++static struct device_driver s3c2410_nand_driver = { ++ .name = "s3c2410-nand", ++ .bus = &platform_bus_type, ++ .probe = s3c2410_nand_probe, ++ .remove = s3c2410_nand_remove, ++}; ++ ++static int __init s3c2410_nand_init(void) ++{ ++ printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); ++ return driver_register(&s3c2410_nand_driver); ++} ++ ++static void __exit s3c2410_nand_exit(void) ++{ ++ driver_unregister(&s3c2410_nand_driver); ++} ++ ++module_init(s3c2410_nand_init); ++module_exit(s3c2410_nand_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ben Dooks "); ++MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/sharpsl.c +@@ -0,0 +1,260 @@ ++/* ++ * drivers/mtd/nand/sharpsl.c ++ * ++ * Copyright (C) 2004 Richard Purdie ++ * ++ * $Id$ ++ * ++ * Based on Sharp's NAND driver sharp_sl.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void __iomem *sharpsl_io_base; ++static int sharpsl_phys_base = 0x0C000000; ++ ++/* register offset */ ++#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ ++#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ ++#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ ++#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ ++#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ ++#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ ++#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ ++ ++/* Flash control bit */ ++#define FLRYBY (1 << 5) ++#define FLCE1 (1 << 4) ++#define FLWP (1 << 3) ++#define FLALE (1 << 2) ++#define FLCLE (1 << 1) ++#define FLCE0 (1 << 0) ++ ++ ++/* ++ * MTD structure for SharpSL ++ */ ++static struct mtd_info *sharpsl_mtd = NULL; ++ ++/* ++ * Define partitions for flash device ++ */ ++#define DEFAULT_NUM_PARTITIONS 3 ++ ++static int nr_partitions; ++static struct mtd_partition sharpsl_nand_default_partition_info[] = { ++ { ++ .name = "System Area", ++ .offset = 0, ++ .size = 7 * 1024 * 1024, ++ }, ++ { ++ .name = "Root Filesystem", ++ .offset = 7 * 1024 * 1024, ++ .size = 30 * 1024 * 1024, ++ }, ++ { ++ .name = "Home Filesystem", ++ .offset = MTDPART_OFS_APPEND , ++ .size = MTDPART_SIZ_FULL , ++ }, ++}; ++ ++/* ++ * hardware specific access to control-lines ++ */ ++static void ++sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd) ++{ ++ switch (cmd) { ++ case NAND_CTL_SETCLE: ++ writeb(readb(FLASHCTL) | FLCLE, FLASHCTL); ++ break; ++ case NAND_CTL_CLRCLE: ++ writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL); ++ break; ++ ++ case NAND_CTL_SETALE: ++ writeb(readb(FLASHCTL) | FLALE, FLASHCTL); ++ break; ++ case NAND_CTL_CLRALE: ++ writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL); ++ break; ++ ++ case NAND_CTL_SETNCE: ++ writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL); ++ break; ++ case NAND_CTL_CLRNCE: ++ writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL); ++ break; ++ } ++} ++ ++static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; ++ ++static struct nand_bbt_descr sharpsl_bbt = { ++ .options = 0, ++ .offs = 4, ++ .len = 2, ++ .pattern = scan_ff_pattern ++}; ++ ++static int ++sharpsl_nand_dev_ready(struct mtd_info* mtd) ++{ ++ return !((readb(FLASHCTL) & FLRYBY) == 0); ++} ++ ++static void ++sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode) ++{ ++ writeb(0 ,ECCCLRR); ++} ++ ++static int ++sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ ecc_code[0] = ~readb(ECCLPUB); ++ ecc_code[1] = ~readb(ECCLPLB); ++ ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; ++ return readb(ECCCNTR) != 0; ++} ++ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++ ++/* ++ * Main initialization routine ++ */ ++int __init ++sharpsl_nand_init(void) ++{ ++ struct nand_chip *this; ++ struct mtd_partition* sharpsl_partition_info; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), ++ GFP_KERNEL); ++ if (!sharpsl_mtd) { ++ printk ("Unable to allocate SharpSL NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* map physical adress */ ++ sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); ++ if(!sharpsl_io_base){ ++ printk("ioremap to access Sharp SL NAND chip failed\n"); ++ kfree(sharpsl_mtd); ++ return -EIO; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&sharpsl_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ sharpsl_mtd->priv = this; ++ ++ /* ++ * PXA initialize ++ */ ++ writeb(readb(FLASHCTL) | FLWP, FLASHCTL); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = FLASHIO; ++ this->IO_ADDR_W = FLASHIO; ++ /* Set address of hardware control function */ ++ this->hwcontrol = sharpsl_nand_hwcontrol; ++ this->dev_ready = sharpsl_nand_dev_ready; ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ /* set eccmode using hardware ECC */ ++ this->eccmode = NAND_ECC_HW3_256; ++ this->enable_hwecc = sharpsl_nand_enable_hwecc; ++ this->calculate_ecc = sharpsl_nand_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ this->badblock_pattern = &sharpsl_bbt; ++ ++ /* Scan to find existence of the device */ ++ err=nand_scan(sharpsl_mtd,1); ++ if (err) { ++ iounmap(sharpsl_io_base); ++ kfree(sharpsl_mtd); ++ return err; ++ } ++ ++ /* Register the partitions */ ++ sharpsl_mtd->name = "sharpsl-nand"; ++ nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, ++ &sharpsl_partition_info, 0); ++ ++ if (nr_partitions <= 0) { ++ nr_partitions = DEFAULT_NUM_PARTITIONS; ++ sharpsl_partition_info = sharpsl_nand_default_partition_info; ++ if (machine_is_poodle()) { ++ sharpsl_partition_info[1].size=30 * 1024 * 1024; ++ } else if (machine_is_corgi() || machine_is_shepherd()) { ++ sharpsl_partition_info[1].size=25 * 1024 * 1024; ++ } else if (machine_is_husky()) { ++ sharpsl_partition_info[1].size=53 * 1024 * 1024; ++ } ++ } ++ ++ if (machine_is_husky()) { ++ /* Need to use small eraseblock size for backward compatibility */ ++ sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS; ++ } ++ ++ add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); ++ ++ /* Return happy */ ++ return 0; ++} ++module_init(sharpsl_nand_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit sharpsl_nand_cleanup(void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1]; ++ ++ /* Release resources, unregister device */ ++ nand_release(sharpsl_mtd); ++ ++ iounmap(sharpsl_io_base); ++ ++ /* Free the MTD device structure */ ++ kfree(sharpsl_mtd); ++} ++module_exit(sharpsl_nand_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Richard Purdie "); ++MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); +--- linux-2.4.21/drivers/mtd/nand/spia.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nand/spia.c +@@ -1,14 +1,14 @@ + /* + * drivers/mtd/nand/spia.c + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * + * 10-29-2001 TG change to support hardwarespecific access + * to controllines (due to change in nand.c) + * page_cache added + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -20,6 +20,8 @@ + * a 64Mibit (8MiB x 8 bits) NAND flash device. + */ + ++#include ++#include + #include + #include + #include +@@ -35,14 +37,14 @@ + /* + * Values specific to the SPIA board (used with EP7212 processor) + */ +-#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ +-#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ +-#define SPIA_PEDR = 0x0080 /* ++#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ ++#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ ++#define SPIA_PEDR 0x0080 /* + * IO offset to Port E data register + * where the CLE, ALE and NCE pins + * are wired to. + */ +-#define SPIA_PEDDR = 0x00c0 /* ++#define SPIA_PEDDR 0x00c0 /* + * IO offset to Port E data direction + * register so we can control the IO + * lines. +@@ -57,26 +59,25 @@ + static int spia_pedr = SPIA_PEDR; + static int spia_peddr = SPIA_PEDDR; + +-MODULE_PARM(spia_io_base, "i"); +-MODULE_PARM(spia_fio_base, "i"); +-MODULE_PARM(spia_pedr, "i"); +-MODULE_PARM(spia_peddr, "i"); +- +-__setup("spia_io_base=",spia_io_base); +-__setup("spia_fio_base=",spia_fio_base); +-__setup("spia_pedr=",spia_pedr); +-__setup("spia_peddr=",spia_peddr); ++module_param(spia_io_base, int, 0); ++module_param(spia_fio_base, int, 0); ++module_param(spia_pedr, int, 0); ++module_param(spia_peddr, int, 0); + + /* + * Define partitions for flash device + */ + const static struct mtd_partition partition_info[] = { +- { name: "SPIA flash partition 1", +- offset: 0, +- size: 2*1024*1024 }, +- { name: "SPIA flash partition 2", +- offset: 2*1024*1024, +- size: 6*1024*1024 } ++ { ++ .name = "SPIA flash partition 1", ++ .offset = 0, ++ .size = 2*1024*1024 ++ }, ++ { ++ .name = "SPIA flash partition 2", ++ .offset = 2*1024*1024, ++ .size = 6*1024*1024 ++ } + }; + #define NUM_PARTITIONS 2 + +@@ -84,7 +85,7 @@ + /* + * hardware specific access to control-lines + */ +-void spia_hwcontrol(int cmd){ ++static void spia_hwcontrol(struct mtd_info *mtd, int cmd){ + + switch(cmd){ + +@@ -131,37 +132,19 @@ + (*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07; + + /* Set address of NAND IO lines */ +- this->IO_ADDR_R = spia_fio_base; +- this->IO_ADDR_W = spia_fio_base; ++ this->IO_ADDR_R = (void __iomem *) spia_fio_base; ++ this->IO_ADDR_W = (void __iomem *) spia_fio_base; + /* Set address of hardware control function */ + this->hwcontrol = spia_hwcontrol; + /* 15 us command delay time */ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (spia_mtd)) { ++ if (nand_scan (spia_mtd, 1)) { + kfree (spia_mtd); + return -ENXIO; + } + +- /* Allocate memory for internal data buffer */ +- this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); +- if (!this->data_buf) { +- printk ("Unable to allocate NAND data buffer for SPIA.\n"); +- kfree (spia_mtd); +- return -ENOMEM; +- } +- +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for SPIA.\n"); +- kfree (this->data_buf); +- kfree (spia_mtd); +- return = -ENOMEM; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); + +@@ -176,14 +159,8 @@ + #ifdef MODULE + static void __exit spia_cleanup (void) + { +- struct nand_chip *this = (struct nand_chip *) &spia_mtd[1]; +- +- /* Unregister the device */ +- del_mtd_device (spia_mtd); +- +- /* Free internal data buffer */ +- kfree (this->data_buf); +- kfree (this->page_cache); ++ /* Release resources, unregister device */ ++ nand_release (spia_mtd); + + /* Free the MTD device structure */ + kfree (spia_mtd); +@@ -192,5 +169,5 @@ + #endif + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * TI fido board. It supports 32MiB and 64MiB cards ++ * ++ * $Id$ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * MTD structure for TOTO board ++ */ ++static struct mtd_info *toto_mtd = NULL; ++ ++static unsigned long toto_io_base = OMAP_FLASH_1_BASE; ++ ++#define CONFIG_NAND_WORKAROUND 1 ++ ++#define NAND_NCE 0x4000 ++#define NAND_CLE 0x1000 ++#define NAND_ALE 0x0002 ++#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE) ++ ++#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0) ++#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE) ++#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */ ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0) ++#else ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE) ++#endif ++#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0) ++#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE) ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info64M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++ { .name = "toto devboard extra partition 4", ++ .offset = 32 * SZ_1M, ++ .size = 32 * SZ_1M }, ++}; ++ ++static struct mtd_partition partition_info32M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++}; ++ ++#define NUM_PARTITIONS32M 3 ++#define NUM_PARTITIONS64M 4 ++/* ++ * hardware specific access to control-lines ++*/ ++ ++static void toto_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ udelay(1); /* hopefully enough time for tc make proceding write to clear */ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; ++ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; ++ ++ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; ++ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; ++ ++ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; ++ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; ++ } ++ udelay(1); /* allow time to ensure gpio state to over take memory write */ ++} ++ ++/* ++ * Main initialization routine ++ */ ++int __init toto_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!toto_mtd) { ++ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&toto_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) toto_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ toto_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = toto_io_base; ++ this->IO_ADDR_W = toto_io_base; ++ this->hwcontrol = toto_hwcontrol; ++ this->dev_ready = NULL; ++ /* 25 us command delay time */ ++ this->chip_delay = 30; ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (toto_mtd, 1)) { ++ err = -ENXIO; ++ goto out_mtd; ++ } ++ ++ /* Register the partitions */ ++ switch(toto_mtd->size){ ++ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; ++ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break; ++ default: { ++ printk (KERN_WARNING "Unsupported Nand device\n"); ++ err = -ENXIO; ++ goto out_buf; ++ } ++ } ++ ++ gpioreserve(NAND_MASK); /* claim our gpios */ ++ archflashwp(0,0); /* open up flash for writing */ ++ ++ goto out; ++ ++out_buf: ++ kfree (this->data_buf); ++out_mtd: ++ kfree (toto_mtd); ++out: ++ return err; ++} ++ ++module_init(toto_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit toto_cleanup (void) ++{ ++ /* Release resources, unregister device */ ++ nand_release (toto_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (toto_mtd); ++ ++ /* stop flash writes */ ++ archflashwp(0,1); ++ ++ /* release gpios to system */ ++ gpiorelease(NAND_MASK); ++} ++module_exit(toto_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Richard Woodruff "); ++MODULE_DESCRIPTION("Glue layer for NAND flash on toto board"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/tx4925ndfmc.c +@@ -0,0 +1,416 @@ ++/* ++ * drivers/mtd/tx4925ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports ++ * 16MiB, 32MiB and 64MiB cards. ++ * ++ * Author: MontaVista Software, Inc. source@mvista.com ++ * ++ * Derived from drivers/mtd/autcpu12.c ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * $Id$ ++ * ++ * Copyright (C) 2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for RBTX4925 board ++ */ ++static struct mtd_info *tx4925ndfmc_mtd = NULL; ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info16k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 8 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info32k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 24 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info64k[] = { ++ { .name = "User FS", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 16 * 0x00100000, ++ .size = 48 * 0x00100000}, ++}; ++ ++static struct mtd_partition partition_info128k[] = { ++ { .name = "Skip bad section", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "User FS", ++ .offset = 16 * 0x00100000, ++ .size = 112 * 0x00100000 }, ++}; ++#define NUM_PARTITIONS16K 2 ++#define NUM_PARTITIONS32K 2 ++#define NUM_PARTITIONS64K 2 ++#define NUM_PARTITIONS128K 2 ++ ++/* ++ * hardware specific access to control-lines ++*/ ++static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_SETNCE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE; ++ break; ++ } ++} ++ ++/* ++* read device ready pin ++*/ ++static int tx4925ndfmc_device_ready(struct mtd_info *mtd) ++{ ++ int ready; ++ ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1; ++ return ready; ++} ++void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ /* reset first */ ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB; ++} ++static void tx4925ndfmc_disable_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++} ++static void tx4925ndfmc_enable_read_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ; ++} ++void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ ++ int i; ++ u_char *ecc = ecc_code; ++ tx4925ndfmc_enable_read_ecc(); ++ for (i = 0;i < 6;i++,ecc++) ++ *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr)); ++ tx4925ndfmc_disable_ecc(); ++} ++void tx4925ndfmc_device_setup(void) ++{ ++ ++ *(unsigned char *)0xbb005000 &= ~0x08; ++ ++ /* reset NDFMC */ ++ tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST; ++ while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST); ++ ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0; ++ tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW; ++} ++static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4925_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4925_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio ++n **pparts, char *); ++#endif ++ ++/* ++ * Main initialization routine ++ */ ++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++int __init tx4925ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4925ndfmc_mtd) { ++ printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ tx4925ndfmc_device_setup(); ++ ++ /* io is indirect via a register so don't need to ioremap address */ ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4925ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr); ++ this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr); ++ this->hwcontrol = tx4925ndfmc_hwcontrol; ++ this->enable_hwecc = tx4925ndfmc_enable_hwecc; ++ this->calculate_ecc = tx4925ndfmc_readecc; ++ this->correct_data = nand_correct_data; ++ this->eccmode = NAND_ECC_HW6_512; ++ this->dev_ready = tx4925ndfmc_device_ready; ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ this->read_byte = tx4925ndfmc_nand_read_byte; ++ this->write_byte = tx4925ndfmc_nand_write_byte; ++ this->cmdfunc = tx4925ndfmc_nand_command; ++ this->write_buf = tx4925ndfmc_nand_write_buf; ++ this->read_buf = tx4925ndfmc_nand_read_buf; ++ this->verify_buf = tx4925ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4925ndfmc_mtd, 1)) { ++ err = -ENXIO; ++ goto out_ior; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4925ndfmc_mtd); ++ } ++#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ switch(tx4925ndfmc_mtd->size){ ++ case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break; ++ case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break; ++ case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; ++ case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; ++ default: { ++ printk ("Unsupported SmartMedia device\n"); ++ err = -ENXIO; ++ goto out_ior; ++ } ++ } ++#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ goto out; ++ ++out_ior: ++out: ++ return err; ++} ++ ++module_init(tx4925ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit tx4925ndfmc_cleanup (void) ++{ ++ /* Release resources, unregister device */ ++ nand_release (tx4925ndfmc_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4925ndfmc_mtd); ++} ++module_exit(tx4925ndfmc_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy "); ++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/nand/tx4938ndfmc.c +@@ -0,0 +1,406 @@ ++/* ++ * drivers/mtd/nand/tx4938ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device connected to ++ * TX4938 internal NAND Memory Controller. ++ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit. ++ * ++ * Author: source@mvista.com ++ * ++ * Based on spia.c by Steven J. Hill ++ * ++ * $Id$ ++ * ++ * Copyright (C) 2000-2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under the ++ * terms of the GNU General Public License version 2. This program is ++ * licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for TX4938 NDFMC ++ */ ++static struct mtd_info *tx4938ndfmc_mtd; ++ ++/* ++ * Define partitions for flash device ++ */ ++#define flush_wb() (void)tx4938_ndfmcptr->mcr; ++ ++#define NUM_PARTITIONS 3 ++#define NUMBER_OF_CIS_BLOCKS 24 ++#define SIZE_OF_BLOCK 0x00004000 ++#define NUMBER_OF_BLOCK_PER_ZONE 1024 ++#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK) ++#ifndef CONFIG_MTD_CMDLINE_PARTS ++/* ++ * You can use the following sample of MTD partitions ++ * on the NAND Flash Memory 32MB or more. ++ * ++ * The following figure shows the image of the sample partition on ++ * the 32MB NAND Flash Memory. ++ * ++ * Block No. ++ * 0 +-----------------------------+ ------ ++ * | CIS | ^ ++ * 24 +-----------------------------+ | ++ * | kernel image | | Zone 0 ++ * | | | ++ * +-----------------------------+ | ++ * 1023 | unused area | v ++ * +-----------------------------+ ------ ++ * 1024 | JFFS2 | ^ ++ * | | | ++ * | | | Zone 1 ++ * | | | ++ * | | | ++ * | | v ++ * 2047 +-----------------------------+ ------ ++ * ++ */ ++static struct mtd_partition partition_info[NUM_PARTITIONS] = { ++ { ++ .name = "RBTX4938 CIS Area", ++ .offset = 0, ++ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK), ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "RBTX4938 kernel image", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */ ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "Root FS (JFFS2)", ++ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */ ++ .size = MTDPART_SIZ_FULL ++ }, ++}; ++#endif ++ ++static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ switch (cmd) { ++ case NAND_CTL_SETCLE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE; ++ break; ++ /* TX4938_NDFMCR_CE bit is 0:high 1:low */ ++ case NAND_CTL_SETNCE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE; ++ break; ++ } ++} ++static int tx4938ndfmc_dev_ready(struct mtd_info *mtd) ++{ ++ flush_wb(); ++ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY); ++} ++static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ; ++ ecc_code[1] = tx4938_ndfmcptr->dtr; ++ ecc_code[0] = tx4938_ndfmcptr->dtr; ++ ecc_code[2] = tx4938_ndfmcptr->dtr; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++} ++static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON; ++} ++ ++static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4938_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4938_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); ++#endif ++/* ++ * Main initialization routine ++ */ ++int __init tx4938ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int bsprt = 0, hold = 0xf, spw = 0xf; ++ int protected = 0; ++ ++ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) { ++ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n"); ++ return -ENODEV; ++ } ++ bsprt = 1; ++ hold = 2; ++ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */ ++ ++ if ((tx4938_ccfgptr->pcfg & ++ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) ++ != TX4938_PCFG_NDF_SEL) { ++ printk("TX4938 NDFMC: disabled by PCFG.\n"); ++ return -ENODEV; ++ } ++ ++ /* reset NDFMC */ ++ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST; ++ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST) ++ ; ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0; ++ tx4938_ndfmcptr->spr = hold << 4 | spw; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4938ndfmc_mtd) { ++ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4938ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->hwcontrol = tx4938ndfmc_hwcontrol; ++ this->dev_ready = tx4938ndfmc_dev_ready; ++ this->calculate_ecc = tx4938ndfmc_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ this->enable_hwecc = tx4938ndfmc_enable_hwecc; ++ this->eccmode = NAND_ECC_HW3_256; ++ this->chip_delay = 100; ++ this->read_byte = tx4938ndfmc_nand_read_byte; ++ this->write_byte = tx4938ndfmc_nand_write_byte; ++ this->cmdfunc = tx4938ndfmc_nand_command; ++ this->write_buf = tx4938ndfmc_nand_write_buf; ++ this->read_buf = tx4938ndfmc_nand_read_buf; ++ this->verify_buf = tx4938ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4938ndfmc_mtd, 1)) { ++ kfree (tx4938ndfmc_mtd); ++ return -ENXIO; ++ } ++ ++ if (protected) { ++ printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); ++ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); ++ } ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4938ndfmc_mtd); ++ } ++#else ++ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS ); ++#endif ++ ++ return 0; ++} ++module_init(tx4938ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit tx4938ndfmc_cleanup (void) ++{ ++ /* Release resources, unregister device */ ++ nand_release (tx4938ndfmc_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4938ndfmc_mtd); ++} ++module_exit(tx4938ndfmc_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy "); ++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC"); +--- linux-2.4.21/drivers/mtd/nftlcore.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nftlcore.c +@@ -1,7 +1,7 @@ + /* Linux driver for NAND Flash Translation Layer */ + /* (c) 1999 Machine Vision Holdings, Inc. */ + /* Author: David Woodhouse */ +-/* $Id$ */ ++/* $Id$ */ + + /* + The contents of this file are distributed under the GNU General +@@ -23,15 +23,13 @@ + #include + #include + #include +-#include ++#include + +-#ifdef CONFIG_KMOD + #include +-#endif + #include + #include + #include +-#include ++#include + + /* maximum number of loops while examining next block, to have a + chance to detect consistency problems (they should never happen +@@ -39,187 +37,107 @@ + + #define MAX_LOOPS 10000 + +-/* NFTL block device stuff */ +-#define MAJOR_NR NFTL_MAJOR +-#define DEVICE_REQUEST nftl_request +-#define DEVICE_OFF(device) +- +- +-#include +-#include +- +-/* Linux-specific block device functions */ +- +-/* I _HATE_ the Linux block device setup more than anything else I've ever +- * encountered, except ... +- */ +- +-static int nftl_sizes[256]; +-static int nftl_blocksizes[256]; +- +-/* .. for the Linux partition table handling. */ +-struct hd_struct part_table[256]; +- +-#if LINUX_VERSION_CODE < 0x20328 +-static void dummy_init (struct gendisk *crap) +-{} +-#endif +- +-static struct gendisk nftl_gendisk = { +- major: MAJOR_NR, +- major_name: "nftl", +- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */ +- max_p: (1<mtd == mtd) { +- /* This is a Spare Media Header for an NFTL we've already found */ +- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n"); ++ if (mtd->type != MTD_NANDFLASH) + return; +- } +- } +- if (firstfree == -1) { +- printk(KERN_WARNING "No more NFTL slot available\n"); ++ /* OK, this is moderately ugly. But probably safe. Alternatives? */ ++ if (memcmp(mtd->name, "DiskOnChip", 10)) ++ return; ++ ++ if (!mtd->block_isbad) { ++ printk(KERN_ERR ++"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" ++"Please use the new diskonchip driver under the NAND subsystem.\n"); + return; + } + ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); ++ + nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); ++ + if (!nftl) { +- printk(KERN_WARNING "Out of memory for NFTL data structures\n"); ++ printk(KERN_WARNING "NFTL: out of memory for data structures\n"); + return; + } ++ memset(nftl, 0, sizeof(*nftl)); + +- init_MUTEX(&nftl->mutex); +- +- nftl->mtd = mtd; ++ nftl->mbd.mtd = mtd; ++ nftl->mbd.devnum = -1; ++ nftl->mbd.blksize = 512; ++ nftl->mbd.tr = tr; ++ memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); ++ nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; + + if (NFTL_mount(nftl) < 0) { +- printk(KERN_WARNING "Could not mount NFTL device\n"); ++ printk(KERN_WARNING "NFTL: could not mount device\n"); + kfree(nftl); + return; + } + + /* OK, it's a new one. Set up all the data structures. */ +-#ifdef PSYCHO_DEBUG +- printk("Found new NFTL nftl%c\n", firstfree + 'a'); +-#endif + +- /* linux stuff */ +- nftl->usecount = 0; ++ /* Calculate geometry */ + nftl->cylinders = 1024; + nftl->heads = 16; + + temp = nftl->cylinders * nftl->heads; +- nftl->sectors = nftl->nr_sects / temp; +- if (nftl->nr_sects % temp) { ++ nftl->sectors = nftl->mbd.size / temp; ++ if (nftl->mbd.size % temp) { + nftl->sectors++; + temp = nftl->cylinders * nftl->sectors; +- nftl->heads = nftl->nr_sects / temp; ++ nftl->heads = nftl->mbd.size / temp; + +- if (nftl->nr_sects % temp) { ++ if (nftl->mbd.size % temp) { + nftl->heads++; + temp = nftl->heads * nftl->sectors; +- nftl->cylinders = nftl->nr_sects / temp; ++ nftl->cylinders = nftl->mbd.size / temp; + } + } + +- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) { +- printk(KERN_WARNING "Cannot calculate an NFTL geometry to " +- "match size of 0x%x.\n", nftl->nr_sects); +- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", ++ if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "NFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", nftl->mbd.size); ++ printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", + nftl->cylinders, nftl->heads , nftl->sectors, +- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors ); +- +- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */ ++ (long)nftl->cylinders * (long)nftl->heads * ++ (long)nftl->sectors ); + } +- NFTLs[firstfree] = nftl; +- /* Finally, set up the block device sizes */ +- nftl_sizes[firstfree * 16] = nftl->nr_sects; +- //nftl_blocksizes[firstfree*16] = 512; +- part_table[firstfree * 16].nr_sects = nftl->nr_sects; +- +- nftl_gendisk.nr_real++; +- +- /* partition check ... */ +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, firstfree); +-#else +- grok_partitions(&nftl_gendisk, firstfree, 1<nr_sects); +-#endif +-} +- +-static void NFTL_unsetup(int i) +-{ +- struct NFTLrecord *nftl = NFTLs[i]; +- +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i); +- +- NFTLs[i] = NULL; + ++ if (add_mtd_blktrans_dev(&nftl->mbd)) { + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); +- +- nftl_gendisk.nr_real--; + kfree(nftl); +-} +- +-/* Search the MTD device for NFTL partitions */ +-static void NFTL_notify_add(struct mtd_info *mtd) +-{ +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name); +- +- if (mtd) { +- if (!mtd->read_oob) { +- /* If this MTD doesn't have out-of-band data, +- then there's no point continuing */ +- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n"); + return; + } +- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n", +- mtd->read, mtd->size, mtd->erasesize); +- +- NFTL_setup(mtd); +- } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif + } + +-static void NFTL_notify_remove(struct mtd_info *mtd) ++static void nftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i; ++ struct NFTLrecord *nftl = (void *)dev; + +- for (i = 0; i < MAX_NFTLS; i++) { +- if (NFTLs[i] && NFTLs[i]->mtd == mtd) +- NFTL_unsetup(i); +- } ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ if (nftl->ReplUnitTable) ++ kfree(nftl->ReplUnitTable); ++ if (nftl->EUNtable) ++ kfree(nftl->EUNtable); ++ kfree(nftl); + } + + #ifdef CONFIG_NFTL_RW +@@ -303,7 +221,7 @@ + + targetEUN = thisEUN; + for (block = 0; block < nftl->EraseSize / 512; block ++) { +- MTD_READOOB(nftl->mtd, ++ MTD_READOOB(nftl->mbd.mtd, + (thisEUN * nftl->EraseSize) + (block * 512), + 16 , &retlen, (char *)&oob); + if (block == 2) { +@@ -420,7 +338,7 @@ + chain by selecting the longer one */ + oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); + oob.u.c.unused = 0xffffffff; +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -444,17 +362,19 @@ + if (BlockMap[block] == BLOCK_NIL) + continue; + +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), +- 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); ++ ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), ++ 512, &retlen, movebuf); + if (ret < 0) { +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) ++ ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, +- movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); ++ movebuf); + if (ret != -EIO) + printk("Error went away on retry.\n"); + } +- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512), +- 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); ++ memset(&oob, 0xff, sizeof(struct nftl_oob)); ++ oob.b.Status = oob.b.Status1 = SECTOR_USED; ++ MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), ++ 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo); + } + + /* add the header so that it is now a valid chain */ +@@ -462,7 +382,7 @@ + = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, + 8, &retlen, (char *)&oob.u); + + /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ +@@ -484,7 +404,6 @@ + + if (NFTL_formatblock(nftl, thisEUN) < 0) { + /* could not erase : mark block as reserved +- * FixMe: Update Bad Unit Table on disk + */ + nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; + } else { +@@ -502,7 +421,7 @@ + return targetEUN; + } + +-u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) ++static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) + { + /* This is the part that needs some cleverness applied. + For now, I'm doing the minimum applicable to actually +@@ -582,7 +501,7 @@ + + lastEUN = writeEUN; + +- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci); + + DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", +@@ -670,12 +589,12 @@ + nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; + + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + +- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + /* we link the new block to the chain only after the +@@ -685,13 +604,13 @@ + /* Both in our cache... */ + nftl->ReplUnitTable[lastEUN] = writeEUN; + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum + = cpu_to_le16(writeEUN); + +- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -704,12 +623,14 @@ + return 0xffff; + } + +-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 writeEUN; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); + size_t retlen; +- u8 eccbuf[6]; ++ struct nftl_oob oob; + + writeEUN = NFTL_findwriteunit(nftl, block); + +@@ -720,16 +641,20 @@ + return 1; + } + +- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, +- 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); +- /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ ++ memset(&oob, 0xff, sizeof(struct nftl_oob)); ++ oob.b.Status = oob.b.Status1 = SECTOR_USED; ++ MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo); ++ /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */ + + return 0; + } + #endif /* CONFIG_NFTL_RW */ + +-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 lastgoodEUN; + u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); +@@ -742,7 +667,7 @@ + + if (thisEUN != BLOCK_NIL) { + while (thisEUN < nftl->nb_blocks) { +- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs, ++ if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -761,13 +686,13 @@ + case SECTOR_IGNORE: + break; + default: +- printk("Unknown status for block %d in EUN %d: %x\n", ++ printk("Unknown status for block %ld in EUN %d: %x\n", + block, thisEUN, status); + break; + } + + if (!silly--) { +- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", ++ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", + block / (nftl->EraseSize / 512)); + return 1; + } +@@ -782,265 +707,22 @@ + } else { + loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; + size_t retlen; +- u_char eccbuf[6]; +- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) ++ if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer)) + return -EIO; + } + return 0; + } + +-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) +-{ +- struct NFTLrecord *nftl; +- int p; +- +- nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS]; +- +- if (!nftl) return -EINVAL; +- +- switch (cmd) { +- case HDIO_GETGEO: { +- struct hd_geometry g; +- +- g.heads = nftl->heads; +- g.sectors = nftl->sectors; +- g.cylinders = nftl->cylinders; +- g.start = part_table[MINOR(inode->i_rdev)].start_sect; +- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; +- } +- case BLKGETSIZE: /* Return device size */ +- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, +- (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9, +- (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (nftl->mtd->sync) +- nftl->mtd->sync(nftl->mtd); +- return 0; +- +- case BLKRRPART: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- if (nftl->usecount > 1) return -EBUSY; +- /* +- * We have to flush all buffers and invalidate caches, +- * or we won't be able to re-use the partitions, +- * if there was a change and we don't want to reboot +- */ +- p = (1< 0) { +- kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p); +- if (part_table[p].nr_sects > 0) +- invalidate_device (devp, 1); +- +- part_table[MINOR(inode->i_dev)+p].start_sect = 0; +- part_table[MINOR(inode->i_dev)+p].nr_sects = 0; +- } +- +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS); +-#else +- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS, +- 1<nr_sects); +-#endif +- return 0; +- +-#if (LINUX_VERSION_CODE < 0x20303) +- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKSSZGET: +- return blk_ioctl(inode->i_rdev, cmd, arg); +-#endif +- +- default: +- return -EINVAL; +- } +-} +- +-void nftl_request(RQFUNC_ARG) +-{ +- unsigned int dev, block, nsect; +- struct NFTLrecord *nftl; +- char *buffer; +- struct request *req; +- int res; +- +- while (1) { +- INIT_REQUEST; /* blk.h */ +- req = CURRENT; +- +- /* We can do this because the generic code knows not to +- touch the request at the head of the queue */ +- spin_unlock_irq(&io_request_lock); +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n"); +- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n", +- (req->cmd == READ) ? "Read " : "Write", +- req->sector, req->current_nr_sectors); +- +- dev = MINOR(req->rq_dev); +- block = req->sector; +- nsect = req->current_nr_sectors; +- buffer = req->buffer; +- res = 1; /* succeed */ +- +- if (dev >= MAX_NFTLS * (1<rq_dev)); +- res = 0; /* fail */ +- goto repeat; +- } +- +- nftl = NFTLs[dev / (1<mutex); +- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); +- +- if (block + nsect > part_table[dev].nr_sects) { +- /* access past the end of device */ +- printk("nftl%c%d: bad access: block = %d, count = %d\n", +- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); +- up(&nftl->mutex); +- res = 0; /* fail */ +- goto repeat; +- } +- +- block += part_table[dev].start_sect; +- +- if (req->cmd == READ) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors); +- +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_readblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n"); +- up(&nftl->mutex); +- goto repeat; +- } else if (req->cmd == WRITE) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, +- req->nr_sectors); +-#ifdef CONFIG_NFTL_RW +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_writeblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n"); +-#else +- res = 0; /* Writes always fail */ +-#endif /* CONFIG_NFTL_RW */ +- up(&nftl->mutex); +- goto repeat; +- } else { +- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- repeat: +- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res); +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static int nftl_open(struct inode *ip, struct file *fp) +-{ +- int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS; +- struct NFTLrecord *thisNFTL; +- thisNFTL = NFTLs[nftlnum]; +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n"); +- +-#ifdef CONFIG_KMOD +- if (!thisNFTL && nftlnum == 0) { +- request_module("docprobe"); +- thisNFTL = NFTLs[nftlnum]; +- } +-#endif +- if (!thisNFTL) { +- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", +- nftlnum, ip->i_rdev, ip, fp); +- return -ENODEV; +- } +- +-#ifndef CONFIG_NFTL_RW +- if (fp->f_mode & FMODE_WRITE) +- return -EROFS; +-#endif /* !CONFIG_NFTL_RW */ +- +- thisNFTL->usecount++; +- BLK_INC_USE_COUNT; +- if (!get_mtd_device(thisNFTL->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- return 0; +-} +- +-static int nftl_release(struct inode *inode, struct file *fp) ++static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct NFTLrecord *thisNFTL; +- +- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); +- +- if (thisNFTL->mtd->sync) +- thisNFTL->mtd->sync(thisNFTL->mtd); +- thisNFTL->usecount--; +- BLK_DEC_USE_COUNT; ++ struct NFTLrecord *nftl = (void *)dev; + +- put_mtd_device(thisNFTL->mtd); ++ geo->heads = nftl->heads; ++ geo->sectors = nftl->sectors; ++ geo->cylinders = nftl->cylinders; + + return 0; + } +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations nftl_fops = { +- read: block_read, +- write: block_write, +- ioctl: nftl_ioctl, +- open: nftl_open, +- release: nftl_release, +- fsync: block_fsync, +-}; +-#else +-static struct block_device_operations nftl_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: nftl_open, +- release: nftl_release, +- ioctl: nftl_ioctl +-}; +-#endif +- +- + + /**************************************************************************** + * +@@ -1048,49 +730,33 @@ + * + ****************************************************************************/ + +-static struct mtd_notifier nftl_notifier = { +- add: NFTL_notify_add, +- remove: NFTL_notify_remove ++ ++static struct mtd_blktrans_ops nftl_tr = { ++ .name = "nftl", ++ .major = NFTL_MAJOR, ++ .part_bits = NFTL_PARTN_BITS, ++ .getgeo = nftl_getgeo, ++ .readsect = nftl_readblock, ++#ifdef CONFIG_NFTL_RW ++ .writesect = nftl_writeblock, ++#endif ++ .add_mtd = nftl_add_mtd, ++ .remove_dev = nftl_remove_dev, ++ .owner = THIS_MODULE, + }; + + extern char nftlmountrev[]; + +-int __init init_nftl(void) ++static int __init init_nftl(void) + { +- int i; +- +-#ifdef PRERELEASE +- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev); +-#endif +- +- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ +- printk("unable to register NFTL block device on major %d\n", MAJOR_NR); +- return -EBUSY; +- } else { +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); +- +- /* set block size to 1kB each */ +- for (i = 0; i < 256; i++) { +- nftl_blocksizes[i] = 1024; +- } +- blksize_size[MAJOR_NR] = nftl_blocksizes; +- +- add_gendisk(&nftl_gendisk); +- } +- +- register_mtd_user(&nftl_notifier); ++ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev); + +- return 0; ++ return register_mtd_blktrans(&nftl_tr); + } + + static void __exit cleanup_nftl(void) + { +- unregister_mtd_user(&nftl_notifier); +- unregister_blkdev(MAJOR_NR, "nftl"); +- +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- +- del_gendisk(&nftl_gendisk); ++ deregister_mtd_blktrans(&nftl_tr); + } + + module_init(init_nftl); +--- linux-2.4.21/drivers/mtd/nftlmount.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/nftlmount.c +@@ -4,7 +4,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id$ ++ * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -21,26 +21,17 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-#define __NO_VERSION__ + #include +-#include + #include +-#include +-#include +-#include +-#include + #include + #include +-#include +-#include + #include + #include + #include +-#include + + #define SECTORSIZE 512 + +-char nftlmountrev[]="$Revision$"; ++char nftlmountrev[]="$Revision$"; + + /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the + * various device information of the NFTL partition and Bad Unit Table. Update +@@ -50,7 +41,6 @@ + static int find_boot_record(struct NFTLrecord *nftl) + { + struct nftl_uci1 h1; +- struct nftl_oob oob; + unsigned int block, boot_record_count = 0; + size_t retlen; + u8 buf[SECTORSIZE]; +@@ -59,8 +49,12 @@ + + /* Assume logical EraseSize == physical erasesize for starting the scan. + We'll sort it out later if we find a MediaHeader which says otherwise */ +- nftl->EraseSize = nftl->mtd->erasesize; +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ /* Actually, we won't. The new DiskOnChip driver has already scanned ++ the MediaHeader and adjusted the virtual erasesize it presents in ++ the mtd device accordingly. We could even get rid of ++ nftl->EraseSize if there were any point in doing so. */ ++ nftl->EraseSize = nftl->mbd.mtd->erasesize; ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + + nftl->MediaUnit = BLOCK_NIL; + nftl->SpareMediaUnit = BLOCK_NIL; +@@ -71,12 +65,15 @@ + + /* Check for ANAND header first. Then can whinge if it's found but later + checks fail */ +- if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { ++ ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf); ++ /* We ignore ret in case the ECC of the MediaHeader is invalid ++ (which is apparently acceptable) */ ++ if (retlen != SECTORSIZE) { + static int warncount = 5; + + if (warncount) { + printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + if (!--warncount) + printk(KERN_WARNING "Further failures for this block will not be printed\n"); + } +@@ -87,16 +84,16 @@ + /* ANAND\0 not found. Continue */ + #if 0 + printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + #endif + continue; + } + + /* To be safer with BIOS, also use erase mark as discriminant */ +- if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, +- 8, &retlen, (char *)&h1)) < 0) { ++ if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ 8, &retlen, (char *)&h1) < 0)) { + printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + +@@ -106,23 +103,23 @@ + */ + if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n", +- block * nftl->EraseSize, nftl->mtd->index, ++ block * nftl->EraseSize, nftl->mbd.mtd->index, + le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1)); + continue; + } + + /* Finally reread to check ECC */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, +- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, ++ &retlen, buf, (char *)&oob, NULL) < 0)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + + /* Paranoia. Check the ANAND header is still there after the ECC read */ + if (memcmp(buf, "ANAND", 6)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + continue; +@@ -137,8 +134,12 @@ + printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n", + nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); + /* if (debug) Print both side by side */ ++ if (boot_record_count < 2) { ++ /* We haven't yet seen two real ones */ + return -1; + } ++ continue; ++ } + if (boot_record_count == 1) + nftl->SpareMediaUnit = block; + +@@ -154,6 +155,10 @@ + memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); + + /* Do some sanity checks on it */ ++#if 0 ++The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual ++erasesize based on UnitSizeFactor. So the erasesize we read from the mtd ++device is already correct. + if (mh->UnitSizeFactor == 0) { + printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); + } else if (mh->UnitSizeFactor < 0xfc) { +@@ -163,9 +168,10 @@ + } else if (mh->UnitSizeFactor != 0xff) { + printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", + mh->UnitSizeFactor); +- nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor); +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + } ++#endif + nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { + printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); +@@ -182,7 +188,7 @@ + return -1; + } + +- nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); ++ nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + + /* If we're not using the last sectors in the device for some reason, + reduce nb_blocks accordingly so we forget they're there */ +@@ -218,11 +224,13 @@ + + /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ + for (i = 0; i < nftl->nb_blocks; i++) { ++#if 0 ++The new DiskOnChip driver already scanned the bad block table. Just query it. + if ((i & (SECTORSIZE - 1)) == 0) { + /* read one sector for every SECTORSIZE of blocks */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize + ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, &retlen, buf, +- (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { ++ (char *)&oob, NULL)) < 0) { + printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", + ret); + kfree(nftl->ReplUnitTable); +@@ -233,6 +241,9 @@ + /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ + if (buf[i & (SECTORSIZE - 1)] != 0xff) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; ++#endif ++ if (nftl->mbd.mtd->block_isbad(nftl->mbd.mtd, i * nftl->EraseSize)) ++ nftl->ReplUnitTable[i] = BLOCK_RESERVED; + } + + nftl->MediaUnit = block; +@@ -257,22 +268,18 @@ + static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, + int check_oob) + { +- int i, retlen; +- u8 buf[SECTORSIZE]; ++ int i; ++ size_t retlen; ++ u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; + + for (i = 0; i < len; i += SECTORSIZE) { +- /* we want to read the sector without ECC check here since a free +- sector does not have ECC syndrome on it yet */ +- if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0) ++ if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0) + return -1; + if (memcmpb(buf, 0xff, SECTORSIZE) != 0) + return -1; + + if (check_oob) { +- if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize, +- &retlen, buf) < 0) +- return -1; +- if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0) ++ if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0) + return -1; + } + address += SECTORSIZE; +@@ -287,7 +294,6 @@ + * Return: 0 when succeed, -1 on error. + * + * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? +- * 2. UnitSizeFactor != 0xFF + */ + int NFTL_formatblock(struct NFTLrecord *nftl, int block) + { +@@ -297,7 +303,7 @@ + struct erase_info *instr = &nftl->instr; + + /* Read the Unit Control Information #1 for Wear-Leveling */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + goto default_uci1; + +@@ -312,16 +318,16 @@ + memset(instr, 0, sizeof(struct erase_info)); + + /* XXX: use async erase interface, XXX: test return code */ ++ instr->mtd = nftl->mbd.mtd; + instr->addr = block * nftl->EraseSize; + instr->len = nftl->EraseSize; +- MTD_ERASE(nftl->mtd, instr); ++ MTD_ERASE(nftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { +- /* could not format, FixMe: We should update the BadUnitTable +- both in memory and on disk */ + printk("Error while formatting block %d\n", block); +- return -1; +- } else { ++ goto fail; ++ } ++ + /* increase and write Wear-Leveling info */ + nb_erases = le32_to_cpu(uci.WearInfo); + nb_erases++; +@@ -334,14 +340,18 @@ + * FixMe: is this check really necessary ? since we have check the + * return code after the erase operation. */ + if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) +- return -1; ++ goto fail; + + uci.WearInfo = le32_to_cpu(nb_erases); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&uci) < 0) +- return -1; ++ goto fail; + return 0; +- } ++fail: ++ /* could not format, update the bad block table (caller is responsible ++ for setting the ReplUnitTable to BLOCK_RESERVED on failure) */ ++ nftl->mbd.mtd->block_markbad(nftl->mbd.mtd, instr->addr); ++ return -1; + } + + /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. +@@ -357,13 +367,14 @@ + { + unsigned int block, i, status; + struct nftl_bci bci; +- int sectors_per_block, retlen; ++ int sectors_per_block; ++ size_t retlen; + + sectors_per_block = nftl->EraseSize / SECTORSIZE; + block = first_block; + for (;;) { + for (i = 0; i < sectors_per_block; i++) { +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -383,7 +394,7 @@ + /* sector not free actually : mark it as SECTOR_IGNORE */ + bci.Status = SECTOR_IGNORE; + bci.Status1 = SECTOR_IGNORE; +- MTD_WRITEOOB(nftl->mtd, ++ MTD_WRITEOOB(nftl->mbd.mtd, + block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci); + } +@@ -446,8 +457,7 @@ + + printk("Formatting block %d\n", block); + if (NFTL_formatblock(nftl, block) < 0) { +- /* cannot format !!!! Mark it as Bad Unit, +- FixMe: update the BadUnitTable on disk */ ++ /* cannot format !!!! Mark it as Bad Unit */ + nftl->ReplUnitTable[block] = BLOCK_RESERVED; + } else { + nftl->ReplUnitTable[block] = BLOCK_FREE; +@@ -476,7 +486,7 @@ + size_t retlen; + + /* check erase mark. */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + +@@ -491,7 +501,7 @@ + h1.EraseMark = cpu_to_le16(ERASE_MARK); + h1.EraseMark1 = cpu_to_le16(ERASE_MARK); + h1.WearInfo = cpu_to_le32(0); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + } else { +@@ -503,7 +513,7 @@ + SECTORSIZE, 0) != 0) + return -1; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i, + 16, &retlen, buf) < 0) + return -1; + if (i == SECTORSIZE) { +@@ -533,7 +543,7 @@ + struct nftl_uci2 uci; + size_t retlen; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + return 0; + +@@ -572,9 +582,9 @@ + + for (;;) { + /* read the block header. If error, we format the chain */ +- if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, + &retlen, (char *)&h0) < 0 || +- MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) { + s->ReplUnitTable[block] = BLOCK_NIL; + do_format_chain = 1; +--- linux-2.4.21/drivers/mtd/redboot.c~mtd-cvs ++++ linux-2.4.21/drivers/mtd/redboot.c +@@ -1,5 +1,5 @@ + /* +- * $Id$ ++ * $Id$ + * + * Parse RedBoot-style Flash Image System (FIS) tables and + * produce a Linux partition array to match. +@@ -7,6 +7,8 @@ + + #include + #include ++#include ++#include + + #include + #include +@@ -28,13 +30,18 @@ + struct fis_list *next; + }; + ++static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; ++module_param(directory, int, 0); ++ + static inline int redboot_checksum(struct fis_image_desc *img) + { + /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ + return 1; + } + +-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts) ++static int parse_redboot_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, ++ unsigned long fis_origin) + { + int nrparts = 0; + struct fis_image_desc *buf; +@@ -43,31 +50,49 @@ + int ret, i; + size_t retlen; + char *names; ++ char *nullname; + int namelen = 0; ++ int nulllen = 0; ++ int numslots; ++ unsigned long offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ static char nullstring[] = "unallocated"; ++#endif + +- buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ++ buf = vmalloc(master->erasesize); + + if (!buf) + return -ENOMEM; + +- /* Read the start of the last erase block */ +- ret = master->read(master, master->size - master->erasesize, +- PAGE_SIZE, &retlen, (void *)buf); ++ if ( directory < 0 ) ++ offset = master->size + directory*master->erasesize; ++ else ++ offset = directory*master->erasesize; ++ ++ printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", ++ master->name, offset); ++ ++ ret = master->read(master, offset, ++ master->erasesize, &retlen, (void *)buf); + + if (ret) + goto out; + +- if (retlen != PAGE_SIZE) { ++ if (retlen != master->erasesize) { + ret = -EIO; + goto out; + } + +- /* RedBoot image could appear in any of the first three slots */ +- for (i = 0; i < 3; i++) { +- if (!memcmp(buf[i].name, "RedBoot", 8)) ++ numslots = (master->erasesize / sizeof(struct fis_image_desc)); ++ for (i = 0; i < numslots; i++) { ++ if (buf[i].name[0] == 0xff) { ++ i = numslots; + break; + } +- if (i == 3) { ++ if (!memcmp(buf[i].name, "FIS directory", 14)) ++ break; ++ } ++ if (i == numslots) { + /* Didn't find it */ + printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", + master->name); +@@ -75,7 +100,7 @@ + goto out; + } + +- for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) { ++ for (i = 0; i < numslots; i++) { + struct fis_list *new_fl, **prev; + + if (buf[i].name[0] == 0xff) +@@ -90,7 +115,11 @@ + goto out; + } + new_fl->img = &buf[i]; ++ if (fis_origin) { ++ buf[i].flash_base -= fis_origin; ++ } else { + buf[i].flash_base &= master->size-1; ++ } + + /* I'm sure the JFFS2 code has done me permanent damage. + * I now think the following is _normal_ +@@ -103,42 +132,69 @@ + + nrparts++; + } +- if (fl->img->flash_base) ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ if (fl->img->flash_base) { + nrparts++; ++ nulllen = sizeof(nullstring); ++ } + + for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { +- if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) ++ if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { + nrparts++; ++ nulllen = sizeof(nullstring); + } +- parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL); ++ } ++#endif ++ parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } +- names = (char *)&parts[nrparts]; +- memset(parts, 0, sizeof(*parts)*nrparts + namelen); ++ ++ memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen); ++ ++ nullname = (char *)&parts[nrparts]; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ if (nulllen > 0) { ++ strcpy(nullname, nullstring); ++ } ++#endif ++ names = nullname + nulllen; ++ + i=0; + ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->img->flash_base) { +- parts[0].name = "unallocated space"; ++ parts[0].name = nullname; + parts[0].size = fl->img->flash_base; + parts[0].offset = 0; ++ i++; + } ++#endif + for ( ; iimg->size; + parts[i].offset = fl->img->flash_base; + parts[i].name = names; + + strcpy(names, fl->img->name); ++#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY ++ if (!memcmp(names, "RedBoot", 8) || ++ !memcmp(names, "RedBoot config", 15) || ++ !memcmp(names, "FIS directory", 14)) { ++ parts[i].mask_flags = MTD_WRITEABLE; ++ } ++#endif + names += strlen(names)+1; + +- if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { + i++; + parts[i].offset = parts[i-1].size + parts[i-1].offset; + parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = "unallocated space"; ++ parts[i].name = nullname; + } ++#endif + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); +@@ -151,11 +207,28 @@ + fl = fl->next; + kfree(old); + } +- kfree(buf); ++ vfree(buf); + return ret; + } + +-EXPORT_SYMBOL(parse_redboot_partitions); ++static struct mtd_part_parser redboot_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_redboot_partitions, ++ .name = "RedBoot", ++}; ++ ++static int __init redboot_parser_init(void) ++{ ++ return register_mtd_parser(&redboot_parser); ++} ++ ++static void __exit redboot_parser_exit(void) ++{ ++ deregister_mtd_parser(&redboot_parser); ++} ++ ++module_init(redboot_parser_init); ++module_exit(redboot_parser_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse "); +--- /dev/null ++++ linux-2.4.21/drivers/mtd/ssfdc.c +@@ -0,0 +1,1132 @@ ++/* ++ * drivers/mtd/ssfdc.c ++ * ++ * Copyright (C) 2003 Simon Haynes (simon@baydel.con) ++ * Baydel Ltd ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * version 2.1 as published by the Free Software Foundation. ++ * ++ * This module provides a translation layer, via mtd, for smart ++ * media card access. It essentially enables the possibility ++ * of using cards on a hardware which does not have a hardware translation ++ * layer and interchanging them with hardware that does ie: PC card readers ++ * ++ * I had to write this module for a specific task and in a short timeframe ++ * for this reason I have imposed some restricions to make the job easier. ++ * ++ * To build an compile the driver I added the following lines ++ * to mtd/Config.in ++ * ++ * dep_tristate ' SSFDC support' CONFIG_SSFDC $CONFIG_MTD ++ * ++ * to /mtd/Makefile ++ * ++ * obj-$(CONFIG_SSFDC) += ssfdc.o ++ * ++ * and compiled the kernel via the usual methods. ++ * ++ * I am sure that there are many problems I don't know about but here are ++ * some that I know of ++ * ++ * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL ++ * I did this because I wanted a static number and I didn't know ++ * how to go about getting a new one. This needs addressing ++ * The dev nodes required are like standard. I only use minor 0 ++ * (/dev/ssfdca), and minor 1 (/dev/ssfdca1). ++ * You should be able to run fdisk on /dev/ssfdca and the first partition ++ * is /dev/ssfdca1. There is no working code in the module for changing the ++ * SMC and rebuilding the maps so the card should not be changed once the ++ * module is loaded. At present I only look for 1 partition. But this is a ++ * small commented hack. ++ * ++ * There is no support cards which do not have a 512 byte page size with 16 ++ * bytes of oob and an erase size of 16K. ++ * There are no checks for this at present. In addition the MTD reported size ++ * must be 16M or a multiple. ++ * ++ * Code to handle multiple partitions or multiple cards is incomplete ++ * Need to allocate data buffer and oob buffer on a per partition basis. ++ * As I am only concerned with one partition I will do this if I ever need to. ++ * The cached physical address variable also needs this attention. ++ * ++ * Recently I have started to work on media changes. Some of this is specific ++ * to my hardware and you will see references to pt_ssfdc_smc and smc_status. ++ * This code is incomplete and does not work. I have commented it for the moment ++ * but it should give an indication of what I think is required. Maybe there is ++ * something it mtd that can help ++ * ++ * 17th August 2004 MHB ++ * ++ * Following updating CVS I noticed some single bit data corruption. I believe ++ * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc ++ * and that mtd->read was applying it's own error corretion from the wrong ecc bytes ++ * I have now corrected this. ++ * ++ * During this time I noticed that while in allocate new I only seem to look for blocks ++ * in 1 zone. So this limits the partition size to 16MB with all the other SMC size ++ * restrictions ++ ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#if (LINUX_VERSION_CODE >= 0x20100) ++#include ++#endif ++#if (LINUX_VERSION_CODE >= 0x20303) ++#include ++#endif ++ ++#include ++ ++#define SSFDC_FORMAT 1 ++ ++#define PDEBUG(fmt, args...) ++ ++#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT ++#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT ++ ++#if (LINUX_VERSION_CODE < 0x20320) ++#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn ++#define blk_init_queue(q, req) q = (req) ++#define blk_cleanup_queue(q) q = NULL ++#define request_arg_t void ++#else ++#define request_arg_t request_queue_t *q ++#endif ++ ++#define TRUE 1 ++#define FALSE 0 ++ ++#define SSFDC_MAJOR 44 ++ ++#define MAJOR_NR SSFDC_MAJOR ++#define DEVICE_NAME "ssfdc" ++#define DEVICE_REQUEST do_ssfdc_request ++#define DEVICE_ON(device) ++#define DEVICE_OFF(device) ++ ++#include ++ ++#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h" ++ ++ ++ ++#define ZONE_SIZE (16 * 1024 * 1024) ++#define SMC_BLOCK_SIZE (16 * 1024) ++#define SECTOR_SIZE 512 ++#define SECTORS_PER_ZONE (ZONE_SIZE / SECTOR_SIZE) ++#define BLOCKS_PER_ZONE (ZONE_SIZE / SMC_BLOCK_SIZE) ++#define SECTORS_PER_BLOCK (SMC_BLOCK_SIZE / SECTOR_SIZE) ++#define OOB_SIZE 16 ++ ++ ++#define MAX_DEVICES 4 ++#define MAX_PARTITIONS 8 ++#define PARTITION_BITS 3 ++#define MAX_ZONES 8 ++ ++ ++int ssfdc_major = SSFDC_MAJOR; ++unsigned int ssfdc_cached = 0xFFFFFFFF; ++static unsigned char ssfdc_scratch[16384]; ++static unsigned char ssfdc_buffer[16]; ++static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; ++static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; ++ ++ ++static struct nand_oobinfo ssfdc_ffoob_info = { ++ .useecc = 0, ++}; ++ ++ ++typedef struct minor_t { ++ atomic_t open; ++ int cached; ++ unsigned char * pt_data; ++ unsigned char * pt_oob; ++} minor_t; ++ ++ ++ ++typedef struct partition_t { ++ int type; ++ struct mtd_info *mtd; ++ int count; ++ unsigned int *zone; ++ unsigned int zoneCount; ++ minor_t minor[MAX_PARTITIONS]; ++ unsigned int last_written[MAX_ZONES]; ++} partition_t; ++ ++partition_t SMCParts[MAX_DEVICES]; ++ ++ ++static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10}; ++ ++static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS]; ++static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS]; ++static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS]; ++smc_control * pt_ssfdc_smc; ++ ++ ++static struct gendisk ssfdc_gendisk = { ++ major: SSFDC_MAJOR, ++ major_name: "ssfdc", ++ minor_shift: PARTITION_BITS, ++ max_p: MAX_PARTITIONS, ++ part: ssfdc_hd, ++ sizes: ssfdc_sizes, ++}; ++ ++ ++static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); ++static int ssfdc_open(struct inode *inode, struct file *file); ++static int ssfdc_close(struct inode *inode, struct file *file); ++static int ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); ++static int ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); ++static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block); ++static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset); ++static int ssfdc_read_partitions(partition_t * pt_smcpart); ++static void ssfdc_notify_add(struct mtd_info *mtd); ++static void ssfdc_notify_remove(struct mtd_info *mtd); ++static void ssfdc_tables(partition_t * pt_smcpart); ++static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc); ++static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone); ++int ssfdc_parity(int number); ++static void ssfdc_erase_callback(struct erase_info *erase); ++ ++ ++ ++static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq); ++ ++ ++static struct mtd_notifier ssfdc_notifier = { ++ add: ssfdc_notify_add, ++ remove: ssfdc_notify_remove, ++}; ++ ++ ++ ++static struct block_device_operations ssfdc_fops = { ++ open: ssfdc_open, ++ release: ssfdc_close, ++ ioctl: ssfdc_ioctl, ++}; ++ ++static struct semaphore ssfdc_semaphore; ++ ++static void ssfdc_notify_add(struct mtd_info *mtd) { ++ ++ ++ ++ ++ if(mtd->index >= 1) return; // Hack to limit SSFDC to 1 partition ++ ++ if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){ ++ PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index); ++ } ++ else { ++ memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t)); ++ SMCParts[mtd->index].mtd = mtd; ++ SMCParts[mtd->index].count = mtd->index; ++ SMCParts[mtd->index].type = 1; ++ SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE; ++ SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL); ++ ++ ++ if(!SMCParts[mtd->index].zone) { ++ printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index); ++ SMCParts[mtd->index].type = 0; ++ } ++ else { ++ memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192); ++ } ++ ++ ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type); ++ } ++ return; ++ ++} ++static int ssfdc_read_partitions(partition_t * pt_smcpart) { ++ ++ int whole, i, j, size; ++ ++//=printk("ssfdc_read_partitions : start\n"); ++ ++ for(i=0; iminor[i].open) > 1)) { ++//=printk("ssfdc_read_partitions : part %d busy\n", i); ++ ++ return -EBUSY; ++ } ++ ++ ++//=printk("ssfdc_read_partitions : tables start\n"); ++ ssfdc_tables(pt_smcpart); ++//=printk("ssfdc_read_partitions : tables end\n"); ++ ++ whole = pt_smcpart->count << PARTITION_BITS; ++ ++ ++ j = MAX_PARTITIONS - 1; ++ while (j-- > 0) { ++ if (ssfdc_hd[whole+j].nr_sects > 0) { ++ kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j); ++ invalidate_device(rdev, 1); ++ } ++ ssfdc_hd[whole+j].start_sect = 0; ++ ssfdc_hd[whole+j].nr_sects = 0; ++ } ++ ++ ++ size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; ++ size /= (0x8 * 0x20); ++ size = size * (0x8 * 0x20); ++ ++//=printk("ssfdc_read_partitions : register start\n"); ++ ++ register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS, ++ &ssfdc_fops, size); ++ ++//=printk("ssfdc_read_partitions : register end\n"); ++ ++ ++ return 0; ++} ++ ++ ++static void ssfdc_notify_remove(struct mtd_info *mtd) { ++int i, j, whole; ++ ++ i=mtd->index; ++ whole = i << PARTITION_BITS; ++ if(SMCParts[i].mtd == mtd) { ++ if(SMCParts[i].zone)kfree(SMCParts[i].zone); ++ memset((void *)&SMCParts[i].type, 0, sizeof(partition_t)); ++ for (j = 0; j < MAX_PARTITIONS; j++) { ++ if (ssfdc_hd[whole+j].nr_sects > 0) { ++ ssfdc_hd[whole+j].start_sect = 0; ++ ssfdc_hd[whole+j].nr_sects=0; ++ } ++ } ++ return; ++ } ++ return; ++} ++ ++ ++ ++static int ssfdc_ioctl(struct inode *inode, struct file *file, ++ u_int cmd, u_long arg) { ++ ++ int minor = MINOR(inode->i_rdev); ++ int ret = -EINVAL; ++ partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type; ++ struct hd_geometry geo; ++ int size; ++/* ++ unsigned char smc_status; ++ ++ smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); ++ if(!(smc_status & SMC_PRESENT)) { ++ printk("ssfdc : media not present\n"); ++ ret = 1; ++ goto ssfdc_ioctl_error; ++ } ++ ++ if(smc_status & SMC_CHANGED) { ++ out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); ++ if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; ++ ssfdc_read_partitions(pt_smcpart); ++ printk("ssfdc : media change\n"); ++ } ++*/ ++ switch(cmd) { ++ ++ case HDIO_GETGEO: ++ memset(&geo, 0, sizeof(geo)); ++ size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; ++ size /= (0x8 * 0x20); ++ geo.heads = 0x8; ++ geo.sectors = 0x20; ++ geo.cylinders = size; ++ geo.start = ssfdc_hd[minor].start_sect; ++// printk(KERN_WARNING "ssfdc : HDIO_GETGEO heads %d, sectors %d, cylinders %d, start %lu\n", ++// geo.heads, geo.sectors, geo.cylinders, geo.start); ++ copy_to_user((void *)arg, &geo, sizeof(geo)); ++ ret = 0; ++ break; ++ ++ case BLKGETSIZE64: ++ case BLKGETSIZE: ++ size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; ++ //=printk(KERN_WARNING "ssfdc : BLKGETSIZE %d, minor %d\n", size, minor); ++ ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); ++ break; ++ case BLKSSZGET: ++ size = 512; ++ ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); ++ break; ++ break; ++ ++ case BLKRRPART: ++ if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; ++ ssfdc_read_partitions(pt_smcpart); ++ ret=0; ++ break; ++ case BLKFLSBUF: ++ printk(KERN_WARNING "ssfdc : block ioctl 0x%x\n", cmd); ++ break; ++ ++ default: ++ printk(KERN_WARNING "ssfdc: unknown ioctl 0x%x\n", cmd); ++ } ++ ++//ssfdc_ioctl_error: ++ return(ret); ++ ++} ++static int ssfdc_open(struct inode *inode, struct file *file) ++{ ++ int minor = MINOR(inode->i_rdev); ++ partition_t *pt_smcpart; ++ int index; ++ ++ if (minor >= MAX_MTD_DEVICES) ++ return -ENODEV; ++ ++ index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; ++ ++ ++ if(SMCParts[index].type != SSFDC_FORMAT) ++ return -ENXIO; ++ ++ pt_smcpart = &SMCParts[index]; ++ ++ ++ if(!pt_smcpart->zone) ++ return -ENXIO; ++ ++ ++ BLK_INC_USE_COUNT; ++ ++ if (!get_mtd_device(pt_smcpart->mtd, -1)) { ++ BLK_DEC_USE_COUNT; ++ return -ENXIO; ++ } ++ ++ if ((file->f_mode & 2) && !(pt_smcpart->mtd->flags & MTD_CLEAR_BITS) ) { ++ put_mtd_device(pt_smcpart->mtd); ++ BLK_DEC_USE_COUNT; ++ return -EROFS; ++ } ++ ++ ++ atomic_inc(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); ++ ++ PDEBUG("ssfdc_open : device %d\n", minor); ++ ++ return(0); ++} ++ ++static void ssfdc_tables(partition_t * pt_smcpart) { ++ ++ int * logical, * physical; ++ int offset = 0; ++ int zone, block; ++ int i, retlen; ++ int block_address, parity; ++ int h, l; ++ ++ for(zone=0; zonezoneCount; zone++) { ++ logical = pt_smcpart->zone + (2048 * zone); ++ memset((void *)logical, 0xFF, 1024 * sizeof(int)); ++ physical = pt_smcpart->zone + (2048 * zone) + 1024; ++ memset((void *)physical, 0xFF, 1024 * sizeof(int)); ++ ++ for(block=0; block < 1024; block++) { ++ offset = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); ++ pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset, sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); ++ if(retlen != sizeof(ssfdc_buffer)) { ++ printk(KERN_WARNING "ssfdc_tables : failed to read OOB\n"); ++ pt_smcpart->type = 0; ++ return; ++ } ++ ++ l = (ssfdc_buffer[7] & 0xFF); ++ h = (ssfdc_buffer[6] & 0xFF); ++ block_address = l + (h << 8L); ++ ++ if((block_address & ~0x7FF) != 0x1000) { ++ continue; ++ } ++ ++ parity = block_address & 0x01; ++ ++ block_address &= 0x7FF; ++ block_address >>= 1; ++ ++ ++ if(ssfdc_parity(block_address) != parity) { ++ printk(KERN_WARNING "ssfdc_tables : parity error offset 0x%x, block 0x%x, parity 0x%x\nOOB : " ++ , offset, block_address, parity); ++ for(i=0; i<16; i++) { ++ printk("0x%02x ", (unsigned char)ssfdc_buffer[i]); ++ } ++ printk("\n"); ++ pt_smcpart->type = 0; ++ return; ++ } ++ ++ ++ /* Ok we have a valid block number so insert it */ ++ *(logical + block_address) = (offset/SMC_BLOCK_SIZE); ++ PDEBUG("ssfdc_tables : logical 0x%x + 0x%x = 0x%x\n", ++ (unsigned int)logical, block_address, (offset/SMC_BLOCK_SIZE)); ++ *(physical + block) = block_address; ++ PDEBUG("ssfdc_tables : physical 0x%x + 0x%x = 0x%x\n", (unsigned int)physical, block, block_address); ++ ++ ++ } ++ } ++ return; ++} ++int ssfdc_parity(int number) { ++ int i; ++ int parity = 1; // the 0x1000 bit ++ ++ for(i=0; i<10; i++) { ++ parity += ((number >> i) & 1); ++ } ++ PDEBUG("ssfdc_parity : number 0x%x, parity 0x%x\n", number, parity); ++ return(parity % 2); ++} ++static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block) { ++ ++ unsigned int * logical; ++ ++ logical = pt_smcpart->zone + (zone * 2048); ++ ++ logical += block; ++ ++ if(*logical == 0xFFFFFFFF) { ++ PDEBUG("ssfdc_physical : physical for zone %d, block %d invalid\n", zone, block); ++ return(-1); ++ } ++ ++ PDEBUG("ssfdc_physical : physical for zone %d, block %d, 0x%x\n", zone, block, (*logical * SMC_BLOCK_SIZE)); ++ return(*logical * SMC_BLOCK_SIZE); ++} ++ ++static int ssfdc_close(struct inode *inode, struct file *file) ++{ ++ int minor = MINOR(inode->i_rdev); ++ partition_t *pt_smcpart; ++ int index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; ++ ++ if (minor >= MAX_MTD_DEVICES) ++ return -ENODEV; ++ ++ if(SMCParts[index].type != SSFDC_FORMAT) ++ return -ENXIO; ++ ++ pt_smcpart = &SMCParts[index]; ++ atomic_dec(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); ++ put_mtd_device(pt_smcpart->mtd); ++ BLK_DEC_USE_COUNT; ++ ++ return(0); ++} ++ ++ ++static void do_ssfdc_request(request_arg_t) ++{ ++ int ret, minor; ++ partition_t *pt_smcpart; ++ int index; ++ do { ++ ++ INIT_REQUEST; ++ ++ ++ ++ minor = MINOR(CURRENT->rq_dev); ++ index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; ++ ++ pt_smcpart = &SMCParts[index]; ++ if (pt_smcpart->type == SSFDC_FORMAT) { ++ ret = 0; ++ switch (CURRENT->cmd) { ++ case READ: ++ ret = ssfdc_read(pt_smcpart, CURRENT->buffer, ++ CURRENT->sector + ssfdc_hd[minor].start_sect, ++ CURRENT->current_nr_sectors); ++ break; ++ ++ case WRITE: ++ ret = ssfdc_write(pt_smcpart, CURRENT->buffer, ++ CURRENT->sector + ssfdc_hd[minor].start_sect, ++ CURRENT->current_nr_sectors); ++ break; ++ ++ default: ++ panic("do_ssfdc_request : unknown block command!\n"); ++ } ++ ++ } else { ++ ret = 1; ++ PDEBUG("not ssfdc partition type\n"); ++ } ++ ++ if (!ret) { ++ CURRENT->sector += CURRENT->current_nr_sectors; ++ } ++ ++ end_request((ret == 0) ? 1 : 0); ++ } while (1); ++} ++ ++static int ssfdc_write(partition_t *pt_smcpart, caddr_t buffer, ++ u_long sector, u_long nblocks) ++{ ++ int zone, block, offset; ++ int sectors_written = 0; ++ int physical; ++ int * pt_logical; ++ int * pt_physical; ++ int new = -1; ++ int size; ++ int retlen; ++ int i; ++ int sc; ++ int ptr_done = 0; ++ unsigned char * ptr = (unsigned char *)buffer; ++ unsigned char ecc_code[6], ecc_calc[6]; ++ int do_erase; ++// unsigned char smc_status; ++ ++ ++ ++ offset = (sector % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; ++ ++ PDEBUG("write device %d, sector %d, count %d\n", ++ pt_smcpart->count, sector, nblocks); ++/* ++ smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); ++ if(!(smc_status & SMC_PRESENT)) { ++ printk("ssfdc : media not present\n"); ++ return -ENXIO; ++ } ++ ++ if(smc_status & SMC_CHANGED) { ++ out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); ++ ssfdc_read_partitions(pt_smcpart); ++ printk("ssfdc : media change\n"); ++ } ++*/ ++ while(sectors_written < nblocks) { ++ ++ new = -1; ++ do_erase = FALSE; ++ ++ zone = (sector + sectors_written) / SECTORS_PER_ZONE; ++ block = ((sector + sectors_written) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; ++ offset = ((sector + sectors_written) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; ++ ++ pt_logical = pt_smcpart->zone + (zone * 2048); ++ pt_physical = pt_smcpart->zone + (zone * 2048) + 1024; ++ ++ size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_written)) ? ++ (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_written); ++ size *= SECTOR_SIZE; ++ ++ PDEBUG("write device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", ++ pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_written, size, (unsigned int)ptr); ++ ++ physical = ssfdc_physical(pt_smcpart, zone, block); ++ ++ ++ if(physical >= 0) { ++ if(ssfdc_cached != physical) { ++ pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, ++ ssfdc_oob_buf, &ssfdc_ffoob_info); ++ if(retlen != SMC_BLOCK_SIZE) { ++ printk(KERN_WARNING "ssfdc_write : failed to read physical\n"); ++ return -ENXIO; ++ } ++ ++ for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); ++ if(retlen != sizeof(ssfdc_buffer)) { ++ printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); ++ return -ENXIO; ++ } ++ ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); ++ for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); ++ } ++ ++ } ++ ++ for(sc=0; sc sc) { ++ PDEBUG("offset %d, sector %d\n", offset, sc); ++ continue; ++ } ++ pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); ++ if(retlen != sizeof(ssfdc_buffer)) { ++ printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); ++ return -ENXIO; ++ } ++ ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); ++ for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); ++ ++ /* find out if the block is being used */ ++ ++ ++ if(ssfdc_sector_blank(pt_smcpart, sc)) { ++ PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, blank, physical 0x%x\n", ++ zone, block, sc, sector, physical); ++ memcpy(&ssfdc_scratch[(sc * SECTOR_SIZE)], ptr+ptr_done, SECTOR_SIZE); ++ nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done), &ecc_calc[0]); ++ nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done + 256), &ecc_calc[3]); ++ for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; ++ i = (block << 1) | 0x1000; ++ i |= ssfdc_parity(block); ++ ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; ++ ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; ++ ++ pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), SECTOR_SIZE, &retlen, ++ ptr + ptr_done, ssfdc_buffer, &ssfdc_ffoob_info); ++ if(retlen != SECTOR_SIZE) { ++ printk(KERN_WARNING "ssfdc_write : failed to write physical 0x%x, sector 0x%x, blank, retlen %d\n" ++ , physical, sc, retlen); ++ return -ENXIO; ++ } ++ ++ ptr_done += SECTOR_SIZE; ++ if(ptr_done >= size) break; ++ } ++ else { ++ new = ssfdc_allocate_new(pt_smcpart, zone); ++ /* erase the old block */ ++ *(pt_physical + ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE)) = 0xFFFFFFFF; ++ ++ PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", ++ (unsigned int)pt_physical, ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE), 0xFFFFFFFF); ++ do_erase = TRUE; ++ PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, written, physical 0x%x, new 0x%x\n", ++ zone, block, sc, sector, physical, new); ++ break; ++ } ++ } ++ } ++ else { ++ ssfdc_cached = 0xFFFFFFFF; ++ memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); ++ new = ssfdc_allocate_new(pt_smcpart, zone); ++ PDEBUG("ssfdc_write : zone %d, block %d, lbn %d, physical 0x%x, unallocated, new 0x%x\n", ++ zone, block, sector, physical, new); ++ } ++ ++ ++ ++ if(new != -1) { ++ ++ ++ memcpy(&ssfdc_scratch[(offset * SECTOR_SIZE)], ptr, size); ++ PDEBUG("ssfdc_write : new 0x%x, offset 0x%x, size 0x%x, block 0x%x\n", new, offset, size, block); ++ for(sc=0; scmtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); ++ for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; ++ i = (block << 1) | 0x1000; ++ i |= ssfdc_parity(block); ++ ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; ++ ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; ++ memcpy(&ssfdc_oob_buf[sc * OOB_SIZE], ssfdc_buffer, OOB_SIZE); ++ } ++ ++ ++ pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, new, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, ++ ssfdc_oob_buf, &ssfdc_ffoob_info); ++ if(retlen != SMC_BLOCK_SIZE) { ++ printk(KERN_WARNING "ssfdc_write : failed to write block, physical 0x%x, returned 0x%x\n", new, retlen); ++ return -ENXIO; ++ } ++ /* change the mapping table to reflect the new block placement */ ++ ++ *(pt_logical + block) = (new % ZONE_SIZE) / SMC_BLOCK_SIZE; ++ PDEBUG("ssfdc_write : logical 0x%x + 0x%x = 0x%x\n", ++ (unsigned int)pt_logical, block, (new % ZONE_SIZE) / SMC_BLOCK_SIZE); ++ ++ *(pt_physical + ((new % ZONE_SIZE) / SMC_BLOCK_SIZE)) = block; ++ PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", ++ (unsigned int)pt_physical, ((new % ZONE_SIZE) / SMC_BLOCK_SIZE), block); ++ ++ ++ ssfdc_cached = new; ++ } ++ ++ ++ ptr += size; ++ ptr_done = 0; ++ sectors_written += (size / SECTOR_SIZE); ++ if(do_erase) ssfdc_erase(pt_smcpart, physical); ++ ++ } ++ ++ ++ ++ ++ return(0); ++} ++static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc) { ++int b; ++ ++ for(b=0; blast_written[zone] + 1; ++ int * pt_physical; ++ int physical; ++ int block; ++ int retlen; ++ unsigned char oob[16]; ++ ++ ++ if(new >= BLOCKS_PER_ZONE) new = 0; ++ ++ ++ while (new != pt_smcpart->last_written[zone]) { ++ block = new % BLOCKS_PER_ZONE; ++ pt_physical = pt_smcpart->zone + (zone * 2048) + 1024 + block; ++ physical = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); ++ ++ PDEBUG("ssfdc_allocate_new : zone %d, block %d, address 0x%08x, data 0x%08x\n", ++ zone, block, (unsigned int)pt_physical, *pt_physical); ++ if(*pt_physical == 0xFFFFFFFF) { ++ PDEBUG("ssfdc_allocate_new : physical 0x%x = 0x%x\n", (unsigned int)pt_physical, *pt_physical); ++ memset(oob, 0, OOB_SIZE); ++ pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical, OOB_SIZE, &retlen, oob); ++ if((oob[5] == 0xFF) && (retlen == OOB_SIZE)) { // If not a bad block ++ pt_smcpart->last_written[zone] = new; ++ return((new * SMC_BLOCK_SIZE) + (zone * ZONE_SIZE)); ++ } ++ else { ++ PDEBUG("ssfdc_allocate_new : new 0x%x, physical 0x%x, block status 0x%x, oob length 0x%x\n", new, physical, oob[5], retlen); ++ } ++ } ++ new++; ++ if(new >= BLOCKS_PER_ZONE) new = 0; ++ } ++ ++ panic("ssfdc_allocate_new : cant find free block\n"); ++ ++} ++ ++ ++ ++static int ssfdc_read(partition_t *pt_smcpart, caddr_t buffer, ++ u_long sector, u_long nblocks) ++{ ++ int zone, block, offset; ++ int sectors_read = 0; ++ int physical; ++ int size; ++ int retlen; ++ int i; ++ int sc; ++ unsigned char * ptr = (unsigned char *)buffer; ++ unsigned char ecc_code[6], ecc_calc[6]; ++/* ++ unsigned char smc_status; ++ ++ smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); ++ if(!(smc_status & SMC_PRESENT)) { ++ printk("ssfdc : media not present\n"); ++ return -ENXIO; ++ } ++ ++ ++ ++ if(smc_status & SMC_CHANGED) { ++ out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); ++ ssfdc_read_partitions(pt_smcpart); ++ printk("ssfdc : media change\n"); ++ } ++*/ ++ while(sectors_read < nblocks) { ++ ++ zone = (sector + sectors_read) / SECTORS_PER_ZONE; ++ block = ((sector + sectors_read) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; ++ offset = ((sector + sectors_read) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; ++ ++ ++ if(offset) { ++ size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_read)) ? ++ (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_read); ++ } ++ else { ++ size = (SECTORS_PER_BLOCK < (nblocks - sectors_read)) ? SECTORS_PER_BLOCK : nblocks - sectors_read; ++ } ++ size *= SECTOR_SIZE; ++ ++ PDEBUG("ssfdc_read : device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", ++ pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_read, size, (unsigned int)ptr); ++ ++ ++ physical = ssfdc_physical(pt_smcpart, zone, block); ++ if(physical >= 0) { ++ if(ssfdc_cached != physical) { ++ pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, ++ ssfdc_oob_buf, &ssfdc_ffoob_info); ++ if(retlen != SMC_BLOCK_SIZE) { ++ printk(KERN_WARNING "ssfdc_read : failed to read physical\n"); ++ return -ENXIO; ++ } ++ for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); ++ if(retlen != sizeof(ssfdc_buffer)) { ++ printk(KERN_WARNING "ssfdc_read : failed to read physical oob\n"); ++ return -ENXIO; ++ } ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); ++ nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); ++ for(i=0; i<3; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; ++ for(i=3; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); ++ nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); ++ } ++ ++ /* Get the ecc bytes and check that they are ok */ ++ ++ ++ } ++ ssfdc_cached = physical; ++ ++ ++ } ++ else { ++ memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); ++ ssfdc_cached = 0xFFFFFFFF; ++ } ++ ++ ++ memcpy(ptr, &ssfdc_scratch[(offset * SECTOR_SIZE)], size); ++ ptr += size; ++ sectors_read += (size / SECTOR_SIZE); ++ } ++ ++ ++ ++ return(0); ++} ++ ++static void ssfdc_erase_callback(struct erase_info *erase) { ++ ++ PDEBUG("ssfdc_erase_callback : wake erase\n"); ++ up(&ssfdc_semaphore); ++ PDEBUG("ssfdc_erase_callback : woken erase\n"); ++} ++ ++static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset) ++{ ++ int ret = 0; ++ struct erase_info *erase; ++ unsigned char * junk; ++ unsigned char * oob; ++ int retlen; ++ int b, sc; ++ ++ ++ PDEBUG("ssfdc_erase : offset 0x%08x\n", offset); ++ ++ erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); ++ junk=kmalloc(pt_smcpart->mtd->erasesize + 16, GFP_KERNEL); ++ oob = junk + pt_smcpart->mtd->erasesize; ++ ++ if (!erase) ++ return -ENOMEM; ++ if (!junk) ++ return -ENOMEM; ++ ++ erase->addr = offset; ++ erase->len = pt_smcpart->mtd->erasesize; ++ erase->callback = ssfdc_erase_callback; ++ ret = pt_smcpart->mtd->erase(pt_smcpart->mtd, erase); ++ if(ret) { ++ printk(KERN_WARNING "ssfdc_erase : failed status 0x%x\n", ret); ++ goto end; ++ ++ } ++ ++ down(&ssfdc_semaphore); ++ ++ pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, offset, SMC_BLOCK_SIZE, &retlen, junk, ++ ssfdc_oob_buf, &ssfdc_ffoob_info); ++ if(retlen != SMC_BLOCK_SIZE) { ++ printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read returned length %d\n", offset, retlen); ++ goto end; ++ } ++ ++ ++ for(sc=0; sc < SECTORS_PER_BLOCK; sc++) { ++ for(b=0; bmtd->read_oob(pt_smcpart->mtd, offset + (sc * SECTOR_SIZE), OOB_SIZE, &retlen, oob); ++ if(retlen != OOB_SIZE) { ++ printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read oob returned length %d\n", offset, retlen); ++ goto end; ++ } ++ for(b=0; bsmc_status); ++*/ ++ memset(ssfdc_ffoob_buf, 0xFF, sizeof(ssfdc_ffoob_buf)); ++ ++ for (i = 0; i < MAX_DEVICES*MAX_PARTITIONS; i++) { ++ ssfdc_hd[i].nr_sects = 0; ++ ssfdc_hd[i].start_sect = 0; ++ ssfdc_blocksizes[i] = 4096; ++ } ++ blksize_size[SSFDC_MAJOR] = ssfdc_blocksizes; ++ ssfdc_gendisk.major = SSFDC_MAJOR; ++ ++ ++ memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); ++ ++ result = register_blkdev(ssfdc_major, "ssfdc", &ssfdc_fops); ++ if(result != 0) { ++ printk(KERN_WARNING "ssfdc : failed to get a major number\n"); ++ return(result); ++ } ++// if(ssfdc_major == 0) ssfdc_major = result; ++ ++ blk_init_queue(BLK_DEFAULT_QUEUE(ssfdc_major), &do_ssfdc_request); ++ ++ add_gendisk(&ssfdc_gendisk); ++ ++ ++ ++ register_mtd_user(&ssfdc_notifier); ++ ++ ++ init_MUTEX_LOCKED(&ssfdc_semaphore); ++ ++ ++ ++ return 0; ++} ++ ++static void __exit cleanup_ssfdc(void) ++{ ++ int i; ++ ++ for(i=0; i"); ++MODULE_DESCRIPTION("SSFDC translation layer support for MTD"); ++ ++ ++ ++ +--- linux-2.4.21/drivers/net/irda/pxa_ir.c~pxa-irda ++++ linux-2.4.21/drivers/net/irda/pxa_ir.c +@@ -38,6 +38,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -786,6 +787,7 @@ + * Suspend the IrDA interface. + */ + ++/* + static int pxa250_irda_shutdown(struct pxa250_irda *si) + { + +@@ -793,6 +795,7 @@ + return 0; + + } ++*/ + + + static int pxa250_irda_suspend(struct net_device *dev, int state) +@@ -1141,11 +1144,11 @@ + /* allocate consistent buffers for dma access + * buffers have to be aligned and situated in dma capable memory region; + */ +- si->rxbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA ,HPSIR_MAX_RXLEN , &si->rxbuf_dma); ++ si->rxbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA ,HPSIR_MAX_RXLEN , &si->rxbuf_dma, 0); + if (! si->rxbuf_dma_virt ) + goto err_rxbuf_dma; + +- si->txbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA, HPSIR_MAX_TXLEN, &si->txbuf_dma); ++ si->txbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA, HPSIR_MAX_TXLEN, &si->txbuf_dma, 0); + if (! si->txbuf_dma_virt ) + goto err_txbuf_dma; + +--- linux-2.4.21/drivers/net/smc91x.c~pxa-smc91x ++++ linux-2.4.21/drivers/net/smc91x.c +@@ -46,10 +46,13 @@ + . 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support + . 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races, + . more bus abstraction, big cleanup, etc. ++ . 20/08/03 Holger Schurig add ethtool support + ----------------------------------------------------------------------------*/ + ++#define DRV_NAME "smc91x" ++ + static const char version[] = +- "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre \n"; ++ DRV_NAME ": v1.1 Aug 20 2003 by Nicolas Pitre \n"; + + /* Debugging level */ + #ifndef SMC_DEBUG +@@ -67,6 +70,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -78,6 +82,7 @@ + #include + #include + #include ++#include + + #include "smc91x.h" + +@@ -105,7 +110,7 @@ + static int irq = SMC_IRQ; + + #ifndef SMC_NOWAIT +-# define SMC_NOWAIT 0 ++# define SMC_NOWAIT 1 + #endif + static int nowait = SMC_NOWAIT; + +@@ -116,6 +121,11 @@ + MODULE_PARM_DESC(irq, "IRQ number"); + MODULE_PARM_DESC(nowait, "set to 1 for no wait state"); + ++static int ++smc_read_phy_register(unsigned long ioaddr, int phyaddr, int phyreg); ++static void ++smc_write_phy_register( unsigned long ioaddr, int phyaddr, ++ int phyreg, int phydata ); + + /*------------------------------------------------------------------------ + . +@@ -143,7 +153,12 @@ + . but to the expense of reduced TX throughput and increased IRQ overhead. + . Note this is not a cure for a too slow data bus or too high IRQ latency. + */ +-#define THROTTLE_TX_PKTS 0 ++#define THROTTLE_TX_PKTS 1 ++ ++/* ++ . This defines if we want to compile ethtool support into the driver ++*/ ++#define WITH_ETHTOOL 1 + + + /* store this information for the driver.. */ +@@ -310,14 +325,14 @@ + if (nowait) + SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT ); + +-#ifdef POWER_DOWN ++#if POWER_DOWN + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_SELECT_BANK( 1 ); + SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_EPH_POWER_EN ); + status = smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); + status &= ~PHY_CNTL_PDN; +- smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); ++ smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, status); + #endif + + /* this should pause enough for the chip to be happy */ +@@ -390,10 +405,10 @@ + SMC_SET_RCR( RCR_CLEAR ); + SMC_SET_TCR( TCR_CLEAR ); + +-#ifdef POWER_DOWN ++#if POWER_DOWN + status = smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); + status |= PHY_CNTL_PDN; +- smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); ++ smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, status); + + /* finally, shut the chip down */ + SMC_SELECT_BANK( 1 ); +@@ -1628,14 +1643,18 @@ + // Setup the default Register Modes + lp->tcr_cur_mode = TCR_DEFAULT; + lp->rcr_cur_mode = RCR_DEFAULT; +- lp->rpc_cur_mode = RPC_DEFAULT; + + /* Set default parameters */ + #ifdef CONFIG_ARCH_RAMSES +- lp->ctl_autoneg = 0; +- lp->ctl_rfduplx = 0; ++ lp->rpc_cur_mode = (RPC_ANEG | (RPC_LED_10 << RPC_LSXA_SHFT) | (RPC_LED_TX_RX << RPC_LSXB_SHFT) | RPC_DPLX); ++ ++ // 10 MBit/S, auto-negotiation only for 10 MB/s ++ lp->ctl_autoneg = 1; ++ lp->ctl_rfduplx = 1; + lp->ctl_rspeed = 10; + #else ++ lp->rpc_cur_mode = RPC_DEFAULT; ++ + lp->ctl_autoneg = 1; + lp->ctl_rfduplx = 1; + lp->ctl_rspeed = 100; +@@ -1680,6 +1699,127 @@ + return 0; + } + ++/*---------------------------------------------------- ++ . smc_ioctl ++ . ++ . This ioctl is currently only used by ethtool(8) to ++ . access the serial EEPROM ++ -----------------------------------------------------*/ ++ ++#if WITH_ETHTOOL ++ ++#define SMC91x_EEPROM_SIZE (0x40*2) ++ ++u16 smc_eeprom_read(long ioaddr, u16 location) ++{ ++ u16 val; ++ u16 oldBank; ++ u16 oldPtr; ++ ++ cli(); ++ // Save chip settings ++ oldBank = SMC_CURRENT_BANK(); ++ SMC_SELECT_BANK( 2 ); ++ oldPtr = SMC_GET_PTR(); ++ ++ // Set location in EEPROM to be read ++ SMC_SET_PTR(location); ++ ++ // Set EEPROM_SELECT and RELOAD bits in control register ++ SMC_SELECT_BANK( 1 ); ++ val = SMC_GET_CTL(); ++ SMC_SET_CTL(val | CTL_EEPROM_SELECT | CTL_RELOAD); ++ ++ // Wait until RELEAD is finished ++ while (SMC_GET_CTL() & CTL_RELOAD) ; ++ ++ // Get EEPROM data ++ val = SMC_inw(ioaddr, GP_REG); ++ ++ // Restore chip settings ++ SMC_SELECT_BANK( 2 ); ++ SMC_SET_PTR(oldPtr); ++ SMC_SELECT_BANK( oldBank ); ++ sti(); ++ ++ return val; ++} ++ ++static int smc_get_eeprom(struct net_device *dev, u8 *buf) ++{ ++ int i; ++ u16 *ebuf = (u16 *)buf; ++ ++ for (i = 0; i < SMC91x_EEPROM_SIZE/2; i++) { ++ ebuf[i] = smc_eeprom_read(dev->base_addr, i); ++ } ++ return 0; ++} ++ ++static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ u32 etcmd; ++ int ret = -EINVAL; ++ ++ if (cmd != SIOCETHTOOL) ++ return -EOPNOTSUPP; ++ ++ if (get_user(etcmd, (u32 *)rq->ifr_data)) ++ return -EFAULT; ++ ++ switch (etcmd) { ++ ++ /* Get driver info */ ++ case ETHTOOL_GDRVINFO: { ++ struct ethtool_drvinfo edrv; ++ ++ memset(&edrv, 0, sizeof(edrv)); ++ edrv.cmd = etcmd; ++ strcpy(edrv.driver, DRV_NAME); ++ sprintf(edrv.bus_info, "ISA:%8.8lx:%d", dev->base_addr, dev->irq); ++ edrv.eedump_len = SMC91x_EEPROM_SIZE; ++ ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv)) ? -EFAULT : 0; ++ break; ++ } ++ ++ /* Get EEPROM data */ ++ case ETHTOOL_GEEPROM: { ++ struct ethtool_eeprom eeprom; ++ u8 eebuf[SMC91x_EEPROM_SIZE]; ++ int r; ++ ++ if (copy_from_user(&eeprom, rq->ifr_data, sizeof(eeprom))) ++ return -EFAULT; ++ ++ if (eeprom.offset > eeprom.offset+eeprom.len) ++ return -EINVAL; ++ ++ if ((eeprom.offset+eeprom.len) > SMC91x_EEPROM_SIZE) { ++ eeprom.len = SMC91x_EEPROM_SIZE-eeprom.offset; ++ } ++ eeprom.magic = 0; ++ if (copy_to_user(rq->ifr_data, &eeprom, sizeof(eeprom))) ++ return -EFAULT; ++ ++ rq->ifr_data += offsetof(struct ethtool_eeprom, data); ++ ++ r = smc_get_eeprom(dev, eebuf); ++ ++ if (r) ++ return r; ++ if (copy_to_user(rq->ifr_data, eebuf+eeprom.offset, eeprom.len)) ++ return -EFAULT; ++ return 0; ++ ++ } ++ } ++ ++ return ret; ++} ++ ++#endif ++ ++ + /*------------------------------------------------------------ + . Get the current statistics. + . This may be called with the card open or closed. +@@ -1925,6 +2065,9 @@ + dev->watchdog_timeo = HZ/10; + dev->get_stats = smc_query_statistics; + dev->set_multicast_list = smc_set_multicast_list; ++#if WITH_ETHTOOL ++ dev->do_ioctl = smc_ioctl; ++#endif + + return 0; + +@@ -1961,12 +2104,17 @@ + smc_shutdown(global_dev); + break; + case PM_RESUME: ++ udelay(5000); + smc_reset(global_dev); + smc_enable(global_dev); + SMC_SELECT_BANK( 1 ); + SMC_SET_MAC_ADDR(global_dev->dev_addr); +- if (lp->version >= 0x70) +- smc_phy_configure(global_dev); ++ if (global_dev->flags & IFF_UP) { ++ if (lp->version >= 0x70) ++ smc_phy_configure(global_dev); ++ } else { ++ smc_shutdown(global_dev); ++ } + break; + } + return 0; +@@ -2054,6 +2202,15 @@ + int ioaddr = RAMSES_ETH_BASE + 0x300; + global_dev->irq = SMC_IRQ; + ret = smc_probe(global_dev, ioaddr); ++#ifdef POWER_DOWN ++ smc_shutdown(global_dev); ++#endif ++ } ++#elif defined(CONFIG_ARCH_RAMSES) ++ { ++ int ioaddr = RAMSES_ETH_BASE + 0x300; ++ global_dev->irq = SMC_IRQ; ++ ret = smc_probe(global_dev, ioaddr); + } + #else + if (global_dev->base_addr == -1) { +@@ -2083,7 +2240,11 @@ + #ifdef CONFIG_PM + if (ret == 0) { + struct smc_local *lp = (struct smc_local *)global_dev->priv; ++#ifdef PM_DEBUG ++ lp->pm = pm_register(PM_SYS_UNKNOWN, 0x73393178, smc_pm_callback, "smc91x"); ++#else + lp->pm = pm_register(PM_SYS_UNKNOWN, 0x73393178, smc_pm_callback); ++#endif + } + #endif + +--- linux-2.4.21/drivers/net/smc91x.h~ramses-smc91x ++++ linux-2.4.21/drivers/net/smc91x.h +@@ -79,6 +79,11 @@ + #include + #define SMC_IOADDR (RAMSES_ETH_PHYS + 0x300) + #define SMC_IRQ ETHERNET_IRQ ++ ++#elif CONFIG_ARCH_RAMSES ++#include ++#define SMC_IOADDR (RAMSES_ETH_PHYS + 0x300) ++#define SMC_IRQ ETHERNET_IRQ + #endif + + #define SMC_CAN_USE_8BIT 1 +--- linux-2.4.21/drivers/net/wireless/hermes.c~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/hermes.c +@@ -52,7 +52,6 @@ + + #include "hermes.h" + +-static char version[] __initdata = "hermes.c: 4 Dec 2002 David Gibson "; + MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); + MODULE_AUTHOR("David Gibson "); + #ifdef MODULE_LICENSE +@@ -226,7 +225,8 @@ + * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware + * + * Callable from any context, but locking is your problem. */ +-int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) ++int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, ++ hermes_response_t *resp) + { + int err; + int k; +@@ -469,13 +469,17 @@ + + err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); + if (err) +- goto out; ++ return err; + + err = hermes_bap_seek(hw, bap, rid, 0); + if (err) +- goto out; ++ return err; + + rlength = hermes_read_reg(hw, dreg); ++ ++ if (! rlength) ++ return -ENOENT; ++ + rtype = hermes_read_reg(hw, dreg); + + if (length) +@@ -495,8 +499,7 @@ + nwords = min((unsigned)rlength - 1, bufsize / 2); + hermes_read_words(hw, dreg, buf, nwords); + +- out: +- return err; ++ return 0; + } + + int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, +@@ -511,7 +514,7 @@ + + err = hermes_bap_seek(hw, bap, rid, 0); + if (err) +- goto out; ++ return err; + + hermes_write_reg(hw, dreg, length); + hermes_write_reg(hw, dreg, rid); +@@ -523,7 +526,6 @@ + err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, + rid, NULL); + +- out: + return err; + } + +@@ -539,9 +541,12 @@ + + static int __init init_hermes(void) + { +- printk(KERN_DEBUG "%s\n", version); +- + return 0; + } + ++static void __exit exit_hermes(void) ++{ ++} ++ + module_init(init_hermes); ++module_exit(exit_hermes); +--- linux-2.4.21/drivers/net/wireless/hermes.h~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/hermes.h +@@ -250,7 +250,6 @@ + u16 scanreason; /* ??? */ + struct hermes_scan_apinfo aps[35]; /* Scan result */ + } __attribute__ ((packed)); +- + #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) + #define HERMES_LINKSTATUS_CONNECTED (0x0001) + #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) +@@ -278,7 +277,7 @@ + + /* Basic control structure */ + typedef struct hermes { +- ulong iobase; ++ unsigned long iobase; + int io_space; /* 1 if we IO-mapped IO, 0 for memory-mapped IO? */ + #define HERMES_IO 1 + #define HERMES_MEM 0 +@@ -368,7 +367,7 @@ + if (hw->io_space) { + insw(hw->iobase + off, buf, count); + } else { +- int i; ++ unsigned i; + u16 *p; + + /* This needs to *not* byteswap (like insw()) but +@@ -388,7 +387,7 @@ + if (hw->io_space) { + outsw(hw->iobase + off, buf, count); + } else { +- int i; ++ unsigned i; + const u16 *p; + + /* This needs to *not* byteswap (like outsw()) but +@@ -401,6 +400,21 @@ + } + } + ++static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count) ++{ ++ unsigned i; ++ ++ off = off << hw->reg_spacing;; ++ ++ if (hw->io_space) { ++ for (i = 0; i < count; i++) ++ outw(0, hw->iobase + off); ++ } else { ++ for (i = 0; i < count; i++) ++ writew(0, hw->iobase + off); ++ } ++} ++ + #define HERMES_READ_RECORD(hw, bap, rid, buf) \ + (hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf))) + #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ +--- linux-2.4.21/drivers/net/wireless/ieee802_11.h~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/ieee802_11.h +@@ -9,6 +9,8 @@ + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ ++ ++ + #define IEEE802_11_HLEN 30 + #define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN) + +--- linux-2.4.21/drivers/net/wireless/orinoco.c~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/orinoco.c +@@ -1,4 +1,4 @@ +-/* orinoco.c 0.13b - (formerly known as dldwd_cs.c and orinoco_cs.c) ++/* orinoco.c 0.13e - (formerly known as dldwd_cs.c and orinoco_cs.c) + * + * A driver for Hermes or Prism 2 chipset based PCMCIA wireless + * adaptors, with Lucent/Agere, Intersil or Symbol firmware. +@@ -117,7 +117,7 @@ + * o Init of priv->tx_rate_ctrl in firmware specific section. + * o Prism2/Symbol rate, upto should be 0xF and not 0x15. Doh ! + * o Spectrum card always need cor_reset (for every reset) +- * o Fix cor_reset to not loose bit 7 in the register ++ * o Fix cor_reset to not lose bit 7 in the register + * o flush_stale_links to remove zombie Pcmcia instances + * o Ack previous hermes event before reset + * Me (with my little hands) +@@ -289,7 +289,7 @@ + * which are used as the dev->open, dev->stop, priv->reset + * callbacks if none are specified when alloc_orinocodev() is + * called. +- * o Removed orinoco_plx_interupt() and orinoco_pci_interrupt(). ++ * o Removed orinoco_plx_interrupt() and orinoco_pci_interrupt(). + * They didn't do anything. + * + * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson +@@ -345,13 +345,54 @@ + * we are connected (avoids cofusing the firmware), and only + * give LINKSTATUS printk()s if the status has changed. + * ++ * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson ++ * o Cleanup: use dev instead of priv in various places. ++ * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event ++ * if we're in the middle of a (driver initiated) hard reset. ++ * o Bug fix: ETH_ZLEN is supposed to include the header ++ * (Dionysus Blazakis & Manish Karir) ++ * o Convert to using workqueues instead of taskqueues (and ++ * backwards compatibility macros for pre 2.5.41 kernels). ++ * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in ++ * airport.c ++ * o New orinoco_tmd.c init module from Joerg Dorchain for ++ * TMD7160 based PCI to PCMCIA bridges (similar to ++ * orinoco_plx.c). ++ * ++ * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson ++ * o Make hw_unavailable a counter, rather than just a flag, this ++ * is necessary to avoid some races (such as a card being ++ * removed in the middle of orinoco_reset(). ++ * o Restore Release/RequestConfiguration in the PCMCIA event handler ++ * when dealing with a driver initiated hard reset. This is ++ * necessary to prevent hangs due to a spurious interrupt while ++ * the reset is in progress. ++ * o Clear the 802.11 header when transmitting, even though we ++ * don't use it. This fixes a long standing bug on some ++ * firmwares, which seem to get confused if that isn't done. ++ * o Be less eager to de-encapsulate SNAP frames, only do so if ++ * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old ++ * behaviour broke CDP (Cisco Discovery Protocol). ++ * o Use dev instead of priv for free_irq() as well as ++ * request_irq() (oops). ++ * o Attempt to reset rather than giving up if we get too many ++ * IRQs. ++ * o Changed semantics of __orinoco_down() so it can be called ++ * safely with hw_unavailable set. It also now clears the ++ * linkstatus (since we're going to have to reassociate). ++ * ++ * v0.13d -> v0.13e - 12 May 2003 - David Gibson ++ * o Support for post-2.5.68 return values from irq handler. ++ * o Fixed bug where underlength packets would be double counted ++ * in the rx_dropped statistics. ++ * o Provided a module parameter to suppress linkstatus messages. ++ * + * TODO +- + * o New wireless extensions API (patch from Moustafa +- * Youssef, updated by Jim Carter). +- * o Fix PCMCIA hard resets with pcmcia-cs. ++ * Youssef, updated by Jim Carter and Pavel Roskin). + * o Handle de-encapsulation within network layer, provide 802.11 + * headers (patch from Thomas 'Dent' Mirlacher) ++ * o RF monitor mode support + * o Fix possible races in SPY handling. + * o Disconnect wireless extensions from fundamental configuration. + * o (maybe) Software WEP support (patch from Stano Meduna). +@@ -373,27 +414,27 @@ + * flag after taking the lock, and if it is set, give up on whatever + * they are doing and drop the lock again. The orinoco_lock() + * function handles this (it unlocks and returns -EBUSY if +- * hw_unavailable is true). */ ++ * hw_unavailable is non-zero). */ + + #include + + #include + #include + #include +-#include + #include + #include + #include + #include + #include +-#include +-#include +-#include + #include + #include + #include + #include + ++#include ++#include ++#include ++ + #include "hermes.h" + #include "hermes_rid.h" + #include "orinoco.h" +@@ -416,6 +457,9 @@ + EXPORT_SYMBOL(orinoco_debug); + #endif + ++static int suppress_linkstatus; /* = 0 */ ++MODULE_PARM(suppress_linkstatus, "i"); ++ + /********************************************************************/ + /* Compile time configuration and compatibility stuff */ + /********************************************************************/ +@@ -443,8 +487,10 @@ + #define USER_BAP 0 + #define IRQ_BAP 1 + #define MAX_IRQLOOPS_PER_IRQ 10 +-#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of how many events the +- device could legitimately generate */ ++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of ++ * how many events the ++ * device could ++ * legitimately generate */ + #define SMALL_KEY_SIZE 5 + #define LARGE_KEY_SIZE 13 + #define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */ +@@ -480,8 +526,8 @@ + {10, 1, 1, 1}, + {20, 0, 2, 2}, + {20, 1, 6, 3}, +- {55, 0, 4, 4}, +- {55, 1, 7, 7}, ++ {55, 0, 4, 4}, ++ {55, 1, 7, 7}, + {110, 0, 5, 8}, + }; + #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) +@@ -522,7 +568,7 @@ + + /* Hardware control routines */ + +-static int __orinoco_program_rids(struct orinoco_private *priv); ++static int __orinoco_program_rids(struct net_device *dev); + + static int __orinoco_hw_set_bitrate(struct orinoco_private *priv); + static int __orinoco_hw_setup_wep(struct orinoco_private *priv); +@@ -535,37 +581,17 @@ + static void __orinoco_set_multicast_list(struct net_device *dev); + + /* Interrupt handling routines */ +-static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); +-static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); ++static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw); ++static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw); + + /* ioctl() routines */ +-static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); +-static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq); +-static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq); +-static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq); +-static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq); +-static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq); +-static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq); +-static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq); +-static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq); +-static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq); +-static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq); +-static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq); +-static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq); +-static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *frq); +-static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *frq); +-static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq); +-static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq); +-static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq); +-static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq); +- +-static int orinoco_debug_dump_recs(struct orinoco_private *priv); ++static int orinoco_debug_dump_recs(struct net_device *dev); + + /********************************************************************/ + /* Function prototypes */ +@@ -577,7 +603,7 @@ + struct hermes *hw = &priv->hw; + int err; + +- err = __orinoco_program_rids(priv); ++ err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_ERR "%s: Error %d configuring card\n", + dev->name, err); +@@ -606,14 +632,25 @@ + + netif_stop_queue(dev); + +- err = hermes_disable_port(hw, 0); +- if (err) { +- printk(KERN_ERR "%s: Error %d disabling MAC port\n", +- dev->name, err); +- return err; ++ if (! priv->hw_unavailable) { ++ if (! priv->broken_disableport) { ++ err = hermes_disable_port(hw, 0); ++ if (err) { ++ /* Some firmwares (e.g. Intersil 1.3.x) seem ++ * to have problems disabling the port, oh ++ * well, too bad. */ ++ printk(KERN_WARNING "%s: Error %d disabling MAC port\n", ++ dev->name, err); ++ priv->broken_disableport = 1; ++ } ++ } ++ hermes_set_irqmask(hw, 0); ++ hermes_write_regn(hw, EVACK, 0xffff); + } +- hermes_set_irqmask(hw, 0); +- hermes_write_regn(hw, EVACK, 0xffff); ++ ++ /* firmware will have to reassociate */ ++ priv->last_linkstatus = 0xffff; ++ priv->connected = 0; + + return 0; + } +@@ -656,38 +693,38 @@ + if (err) + return err; + +- priv->open = 1; +- + err = __orinoco_up(dev); + ++ if (! err) ++ priv->open = 1; ++ + orinoco_unlock(priv, &flags); + + return err; + } + +-static int orinoco_stop(struct net_device *dev) ++int orinoco_stop(struct net_device *dev) + { + struct orinoco_private *priv = dev->priv; + int err = 0; + + /* We mustn't use orinoco_lock() here, because we need to be +- able to close the interface, even if hw_unavailable is set ++ able to close the interface even if hw_unavailable is set + (e.g. as we're released after a PC Card removal) */ + spin_lock_irq(&priv->lock); + + priv->open = 0; + +- if (! priv->hw_unavailable) +- err = __orinoco_down(dev); ++ err = __orinoco_down(dev); + + spin_unlock_irq(&priv->lock); + + return err; + } + +-static int __orinoco_program_rids(struct orinoco_private *priv) ++static int __orinoco_program_rids(struct net_device *dev) + { +- struct net_device *dev = priv->ndev; ++ struct orinoco_private *priv = dev->priv; + hermes_t *hw = &priv->hw; + int err; + struct hermes_idstring idbuf; +@@ -873,51 +910,84 @@ + } + + /* xyzzy */ +-static int orinoco_reconfigure(struct orinoco_private *priv) ++static int orinoco_reconfigure(struct net_device *dev) + { ++ struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; + +- orinoco_lock(priv, &flags); ++ if (priv->broken_disableport) { ++ schedule_work(&priv->reset_work); ++ return 0; ++ } ++ ++ err = orinoco_lock(priv, &flags); ++ if (err) ++ return err; + ++ + err = hermes_disable_port(hw, 0); + if (err) { +- printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", +- priv->ndev->name); ++ printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", ++ dev->name); ++ priv->broken_disableport = 1; + goto out; + } + +- err = __orinoco_program_rids(priv); +- if (err) ++ err = __orinoco_program_rids(dev); ++ if (err) { ++ printk(KERN_WARNING "%s: Unable to reconfigure card\n", ++ dev->name); + goto out; ++ } + + err = hermes_enable_port(hw, 0); + if (err) { +- printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", +- priv->ndev->name); ++ printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", ++ dev->name); + goto out; + } + + out: ++ if (err) { ++ printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); ++ schedule_work(&priv->reset_work); ++ err = 0; ++ } ++ + orinoco_unlock(priv, &flags); + return err; + + } + + /* This must be called from user context, without locks held - use +- * schedule_task() */ ++ * schedule_work() */ + static void orinoco_reset(struct net_device *dev) + { + struct orinoco_private *priv = dev->priv; ++ struct hermes *hw = &priv->hw; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) ++ /* When the hardware becomes available again, whatever ++ * detects that is responsible for re-initializing ++ * it. So no need for anything further*/ + return; + +- priv->hw_unavailable = 1; ++ netif_stop_queue(dev); ++ ++ /* Shut off interrupts. Depending on what state the hardware ++ * is in, this might not work, but we'll try anyway */ ++ hermes_set_irqmask(hw, 0); ++ hermes_write_regn(hw, EVACK, 0xffff); ++ ++ priv->hw_unavailable++; ++ priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ ++ priv->connected = 0; ++ + orinoco_unlock(priv, &flags); + + if (priv->hard_reset) +@@ -936,18 +1006,22 @@ + return; + } + +- spin_lock_irqsave(&priv->lock, flags); ++ spin_lock_irq(&priv->lock); /* This has to be called from user context */ + +- priv->hw_unavailable = 0; ++ priv->hw_unavailable--; + +- err = __orinoco_up(dev); +- if (err) { +- printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", +- dev->name, err); +- } else +- dev->trans_start = jiffies; ++ /* priv->open or priv->hw_unavailable might have changed while ++ * we dropped the lock */ ++ if (priv->open && (! priv->hw_unavailable)) { ++ err = __orinoco_up(dev); ++ if (err) { ++ printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", ++ dev->name, err); ++ } else ++ dev->trans_start = jiffies; ++ } + +- orinoco_unlock(priv, &flags); ++ spin_unlock_irq(&priv->lock); + + return; + } +@@ -979,10 +1053,18 @@ + } + } + ++/* Does the frame have a SNAP header indicating it should be ++ * de-encapsulated to Ethernet-II? */ + static inline int +-is_snap(struct header_struct *hdr) ++is_ethersnap(struct header_struct *hdr) + { +- return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); ++ /* We de-encapsulate all packets which, a) have SNAP headers ++ * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header ++ * and where b) the OUI of the SNAP header is 00:00:00 or ++ * 00:00:f8 - we need both because different APs appear to use ++ * different OUIs for some reason */ ++ return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) ++ && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); + } + + static void +@@ -1140,7 +1222,8 @@ + return 0; + } + +-static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]) ++static int orinoco_hw_get_bssid(struct orinoco_private *priv, ++ char buf[ETH_ALEN]) + { + hermes_t *hw = &priv->hw; + int err = 0; +@@ -1159,7 +1242,7 @@ + } + + static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, +- char buf[IW_ESSID_MAX_SIZE+1]) ++ char buf[IW_ESSID_MAX_SIZE+1]) + { + hermes_t *hw = &priv->hw; + int err = 0; +@@ -1236,9 +1319,8 @@ + } + + if ( (channel < 1) || (channel > NUM_CHANNELS) ) { +- struct net_device *dev = priv->ndev; +- +- printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel); ++ printk(KERN_WARNING "%s: Channel out of range (%d)!\n", ++ priv->ndev->name, channel); + err = -EBUSY; + goto out; + +@@ -1253,8 +1335,8 @@ + return err ? err : freq; + } + +-static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, +- s32 *rates, int max) ++static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, ++ int *numrates, s32 *rates, int max) + { + hermes_t *hw = &priv->hw; + struct hermes_idstring list; +@@ -1287,9 +1369,6 @@ + } + + #if 0 +-#ifndef ORINOCO_DEBUG +-static inline void show_rx_frame(struct orinoco_rxframe_hdr *frame) {} +-#else + static void show_rx_frame(struct orinoco_rxframe_hdr *frame) + { + printk(KERN_DEBUG "RX descriptor:\n"); +@@ -1346,17 +1425,16 @@ + frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); + printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype); + } +-#endif +-#endif ++#endif /* 0 */ + + /* + * Interrupt handler + */ +-void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) + { +- struct orinoco_private *priv = (struct orinoco_private *) dev_id; ++ struct net_device *dev = (struct net_device *)dev_id; ++ struct orinoco_private *priv = dev->priv; + hermes_t *hw = &priv->hw; +- struct net_device *dev = priv->ndev; + int count = MAX_IRQLOOPS_PER_IRQ; + u16 evstat, events; + /* These are used to detect a runaway interrupt situation */ +@@ -1367,12 +1445,17 @@ + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) { +- /* If hw is unavailable */ +- return; ++ /* If hw is unavailable - we don't know if the irq was ++ * for us or not */ ++ return IRQ_HANDLED; + } + + evstat = hermes_read_regn(hw, EVSTAT); + events = evstat & hw->inten; ++ if (! events) { ++ orinoco_unlock(priv, &flags); ++ return IRQ_NONE; ++ } + + if (jiffies != last_irq_jiffy) + loops_this_jiffy = 0; +@@ -1380,11 +1463,11 @@ + + while (events && count--) { + if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { +- printk(KERN_CRIT "%s: IRQ handler is looping too \ +-much! Shutting down.\n", +- dev->name); +- /* Perform an emergency shutdown */ ++ printk(KERN_WARNING "%s: IRQ handler is looping too " ++ "much! Resetting.\n", dev->name); ++ /* Disable interrupts for now */ + hermes_set_irqmask(hw, 0); ++ schedule_work(&priv->reset_work); + break; + } + +@@ -1395,21 +1478,21 @@ + } + + if (events & HERMES_EV_TICK) +- __orinoco_ev_tick(priv, hw); ++ __orinoco_ev_tick(dev, hw); + if (events & HERMES_EV_WTERR) +- __orinoco_ev_wterr(priv, hw); ++ __orinoco_ev_wterr(dev, hw); + if (events & HERMES_EV_INFDROP) +- __orinoco_ev_infdrop(priv, hw); ++ __orinoco_ev_infdrop(dev, hw); + if (events & HERMES_EV_INFO) +- __orinoco_ev_info(priv, hw); ++ __orinoco_ev_info(dev, hw); + if (events & HERMES_EV_RX) +- __orinoco_ev_rx(priv, hw); ++ __orinoco_ev_rx(dev, hw); + if (events & HERMES_EV_TXEXC) +- __orinoco_ev_txexc(priv, hw); ++ __orinoco_ev_txexc(dev, hw); + if (events & HERMES_EV_TX) +- __orinoco_ev_tx(priv, hw); ++ __orinoco_ev_tx(dev, hw); + if (events & HERMES_EV_ALLOC) +- __orinoco_ev_alloc(priv, hw); ++ __orinoco_ev_alloc(dev, hw); + + hermes_write_regn(hw, EVACK, events); + +@@ -1418,30 +1501,34 @@ + }; + + orinoco_unlock(priv, &flags); ++ return IRQ_HANDLED; + } + +-static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) + { +- printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name); ++ printk(KERN_DEBUG "%s: TICK\n", dev->name); + } + +-static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) + { + /* This seems to happen a fair bit under load, but ignoring it + seems to work fine...*/ + printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", +- priv->ndev->name); ++ dev->name); + } + +-static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) + { +- printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name); ++ printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); + } + + static void print_linkstatus(struct net_device *dev, u16 status) + { + char * s; + ++ if (suppress_linkstatus) ++ return; ++ + switch (status) { + case HERMES_LINKSTATUS_NOT_CONNECTED: + s = "Not Connected"; +@@ -1472,9 +1559,9 @@ + dev->name, s, status); + } + +-static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) + { +- struct net_device *dev = priv->ndev; ++ struct orinoco_private *priv = dev->priv; + u16 infofid; + struct { + u16 len; +@@ -1573,9 +1660,9 @@ + } + } + +-static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) + { +- struct net_device *dev = priv->ndev; ++ struct orinoco_private *priv = dev->priv; + struct net_device_stats *stats = &priv->stats; + struct iw_statistics *wstats = &priv->wstats; + struct sk_buff *skb = NULL; +@@ -1664,14 +1751,13 @@ + * So, check ourselves */ + if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || + ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || +- is_snap(&hdr)) { ++ is_ethersnap(&hdr)) { + /* These indicate a SNAP within 802.2 LLC within + 802.11 frame which we'll need to de-encapsulate to + the original EthernetII frame. */ + + if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ + stats->rx_length_errors++; +- stats->rx_dropped++; + goto drop; + } + +@@ -1726,9 +1812,9 @@ + return; + } + +-static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) + { +- struct net_device *dev = priv->ndev; ++ struct orinoco_private *priv = dev->priv; + struct net_device_stats *stats = &priv->stats; + u16 fid = hermes_read_regn(hw, TXCOMPLFID); + struct hermes_tx_descriptor desc; +@@ -1752,8 +1838,9 @@ + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); + } + +-static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) + { ++ struct orinoco_private *priv = dev->priv; + struct net_device_stats *stats = &priv->stats; + + stats->tx_packets++; +@@ -1761,9 +1848,10 @@ + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); + } + +-static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw) ++static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) + { +- struct net_device *dev = priv->ndev; ++ struct orinoco_private *priv = dev->priv; ++ + u16 fid = hermes_read_regn(hw, ALLOCFID); + + if (fid != priv->txfid) { +@@ -1945,7 +2033,7 @@ + + TRACE_ENTER(dev->name); + +- /* No need to lock, the resetting flag is already set in ++ /* No need to lock, the hw_unavailable flag is already set in + * alloc_orinocodev() */ + priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; + +@@ -2081,8 +2169,6 @@ + priv->wep_on = 0; + priv->tx_key = 0; + +- priv->hw_unavailable = 0; +- + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO) { + /* Try workaround for old Symbol firmware bug */ +@@ -2102,6 +2188,12 @@ + goto out; + } + ++ /* Make the hardware available, as long as it hasn't been ++ * removed elsewhere (e.g. by PCMCIA hot unplug) */ ++ spin_lock_irq(&priv->lock); ++ priv->hw_unavailable--; ++ spin_unlock_irq(&priv->lock); ++ + printk(KERN_DEBUG "%s: ready\n", dev->name); + + out: +@@ -2267,7 +2359,7 @@ + + /* Length of the packet body */ + /* FIXME: what if the skb is smaller than this? */ +- len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN); ++ len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN); + + eh = (struct ethhdr *)skb->data; + +@@ -2281,6 +2373,12 @@ + goto fail; + } + ++ /* Clear the 802.11 header and data length fields - some ++ * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused ++ * if this isn't done. */ ++ hermes_clear_words(hw, HERMES_DATA0, ++ HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); ++ + /* Encapsulate Ethernet-II frames */ + if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */ + struct header_struct hdr; +@@ -2362,7 +2460,7 @@ + + stats->tx_errors++; + +- schedule_task(&priv->timeout_task); ++ schedule_work(&priv->reset_work); + } + + static int +@@ -2532,7 +2630,7 @@ + } + + err = orinoco_hw_get_bitratelist(priv, &numrates, +- range.bitrate, IW_MAX_BITRATES); ++ range.bitrate, IW_MAX_BITRATES); + if (err) + return err; + range.num_bitrates = numrates; +@@ -2799,7 +2897,7 @@ + erq->flags = 1; + erq->length = strlen(essidbuf) + 1; + if (erq->pointer) +- if ( copy_to_user(erq->pointer, essidbuf, erq->length) ) ++ if (copy_to_user(erq->pointer, essidbuf, erq->length)) + return -EFAULT; + + TRACE_EXIT(dev->name); +@@ -3128,7 +3226,7 @@ + rrq->value = 5500000; + else + rrq->value = val * 1000000; +- break; ++ break; + case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ + case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ + for (i = 0; i < BITRATE_TABLE_SIZE; i++) +@@ -3754,7 +3852,7 @@ + + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); + +- schedule_task(&priv->timeout_task); ++ schedule_work(&priv->reset_work); + break; + + case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ +@@ -3827,7 +3925,7 @@ + break; + + case SIOCIWLASTPRIV: +- err = orinoco_debug_dump_recs(priv); ++ err = orinoco_debug_dump_recs(dev); + if (err) + printk(KERN_ERR "%s: Unable to dump records (%d)\n", + dev->name, err); +@@ -3839,7 +3937,7 @@ + } + + if (! err && changed && netif_running(dev)) { +- err = orinoco_reconfigure(priv); ++ err = orinoco_reconfigure(dev); + } + + TRACE_EXIT(dev->name); +@@ -3924,7 +4022,7 @@ + DEBUG_REC(PRIID,WORDS), + DEBUG_REC(PRISUPRANGE,WORDS), + DEBUG_REC(CFIACTRANGES,WORDS), +- DEBUG_REC(NICSERNUM,WORDS), ++ DEBUG_REC(NICSERNUM,XSTRING), + DEBUG_REC(NICID,WORDS), + DEBUG_REC(MFISUPRANGE,WORDS), + DEBUG_REC(CFISUPRANGE,WORDS), +@@ -3961,8 +4059,9 @@ + + #define DEBUG_LTV_SIZE 128 + +-static int orinoco_debug_dump_recs(struct orinoco_private *priv) ++static int orinoco_debug_dump_recs(struct net_device *dev) + { ++ struct orinoco_private *priv = dev->priv; + hermes_t *hw = &priv->hw; + u8 *val8; + u16 *val16; +@@ -4051,6 +4150,7 @@ + dev->do_ioctl = orinoco_ioctl; + dev->change_mtu = orinoco_change_mtu; + dev->set_multicast_list = orinoco_set_multicast_list; ++ /* we use the default eth_mac_addr for setting the MAC addr */ + + /* Set up default callbacks */ + dev->open = orinoco_open; +@@ -4062,7 +4162,7 @@ + priv->hw_unavailable = 1; /* orinoco_init() must clear this + * before anything else touches the + * hardware */ +- INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev); ++ INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); + + priv->last_linkstatus = 0xffff; + priv->connected = 0; +@@ -4079,13 +4179,14 @@ + + EXPORT_SYMBOL(__orinoco_up); + EXPORT_SYMBOL(__orinoco_down); ++EXPORT_SYMBOL(orinoco_stop); + EXPORT_SYMBOL(orinoco_reinit_firmware); + + EXPORT_SYMBOL(orinoco_interrupt); + + /* Can't be declared "const" or the whole __initdata section will + * become const */ +-static char version[] __initdata = "orinoco.c 0.13b (David Gibson and others)"; ++static char version[] __initdata = "orinoco.c 0.13e (David Gibson and others)"; + + static int __init init_orinoco(void) + { +--- linux-2.4.21/drivers/net/wireless/orinoco.h~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/orinoco.h +@@ -11,9 +11,29 @@ + #include + #include + #include +-#include ++#include + #include "hermes.h" + ++/* Workqueue / task queue backwards compatibility stuff */ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) ++#include ++#else ++#include ++#define work_struct tq_struct ++#define INIT_WORK INIT_TQUEUE ++#define schedule_work schedule_task ++#endif ++ ++/* Interrupt handler backwards compatibility stuff */ ++#ifndef IRQ_NONE ++ ++#define IRQ_NONE ++#define IRQ_HANDLED ++typedef void irqreturn_t; ++ ++#endif ++ + /* To enable debug messages */ + //#define ORINOCO_DEBUG 3 + +@@ -36,13 +56,13 @@ + + + struct orinoco_private { +- void *card; /* Pointer to card dependant structure */ ++ void *card; /* Pointer to card dependent structure */ + int (*hard_reset)(struct orinoco_private *); + + /* Synchronisation stuff */ + spinlock_t lock; + int hw_unavailable; +- struct tq_struct timeout_task; ++ struct work_struct reset_work; + + /* driver state */ + int open; +@@ -72,6 +92,7 @@ + int has_sensitivity; + int nicbuf_size; + u16 channel_mask; ++ int broken_disableport; + + /* Configuration paramaters */ + u32 iw_mode; +@@ -111,9 +132,9 @@ + int (*hard_reset)(struct orinoco_private *)); + extern int __orinoco_up(struct net_device *dev); + extern int __orinoco_down(struct net_device *dev); +-int orinoco_reinit_firmware(struct net_device *dev); +- +-extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); ++extern int orinoco_stop(struct net_device *dev); ++extern int orinoco_reinit_firmware(struct net_device *dev); ++extern irqreturn_t orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); + + /********************************************************************/ + /* Locking and synchronization functions */ +--- linux-2.4.21/drivers/net/wireless/orinoco_cs.c~orinoco013e ++++ linux-2.4.21/drivers/net/wireless/orinoco_cs.c +@@ -1,4 +1,4 @@ +-/* orinoco_cs.c 0.13b - (formerly known as dldwd_cs.c) ++/* orinoco_cs.c 0.13e - (formerly known as dldwd_cs.c) + * + * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such + * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ +@@ -22,11 +22,7 @@ + #include + #include + #include +-#include + #include +-#include +-#include +-#include + #include + #include + #include +@@ -38,7 +34,10 @@ + #include + #include + #include +-#include ++ ++#include ++#include ++#include + + #include "orinoco.h" + +@@ -62,7 +61,7 @@ + + /* Some D-Link cards have buggy CIS. They do work at 5v properly, but + * don't have any CIS entry for it. This workaround it... */ +-static int ignore_cis_vcc; /* = 0 */ ++static int ignore_cis_vcc = 1; + + MODULE_PARM(irq_mask, "i"); + MODULE_PARM(irq_list, "1-4i"); +@@ -145,8 +144,10 @@ + /* PCMCIA stuff */ + /********************************************************************/ + ++/* In 2.5 (as of 2.5.69 at least) there is a cs_error exported which ++ * does this, but it's not in 2.4 so we do our own for now. */ + static void +-cs_error(client_handle_t handle, int func, int ret) ++orinoco_cs_error(client_handle_t handle, int func, int ret) + { + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +@@ -202,6 +203,7 @@ + link->priv = dev; + + /* Initialize the dev_link_t structure */ ++ init_timer(&link->release); + link->release.function = &orinoco_cs_release; + link->release.data = (u_long) link; + +@@ -240,7 +242,7 @@ + + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { +- cs_error(link->handle, RegisterClient, ret); ++ orinoco_cs_error(link->handle, RegisterClient, ret); + orinoco_cs_detach(link); + return NULL; + } +@@ -269,19 +271,12 @@ + return; + } + +- /* +- If the device is currently configured and active, we won't +- actually delete it yet. Instead, it is marked so that when +- the release() function is called, that will trigger a proper +- detach(). +- */ + if (link->state & DEV_CONFIG) { +-#ifdef PCMCIA_DEBUG +- printk(KERN_DEBUG "orinoco_cs: detach postponed, '%s' " +- "still locked\n", link->dev->dev_name); +-#endif +- link->state |= DEV_STALE_LINK; +- return; ++ orinoco_cs_release((u_long)link); ++ if (link->state & DEV_CONFIG) { ++ link->state |= DEV_STALE_LINK; ++ return; ++ } + } + + /* Break the link with Card Services */ +@@ -368,7 +363,7 @@ + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); +- cistpl_cftable_entry_t dflt = { index: 0 }; ++ cistpl_cftable_entry_t dflt = { .index = 0 }; + + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); +@@ -472,7 +467,7 @@ + link->irq.IRQInfo2 |= 1 << irq_list[i]; + + link->irq.Handler = orinoco_interrupt; +- link->irq.Instance = priv; ++ link->irq.Instance = dev; + + CS_CHECK(RequestIRQ, link->handle, &link->irq); + } +@@ -532,7 +527,7 @@ + return; + + cs_failed: +- cs_error(link->handle, last_fn, last_ret); ++ orinoco_cs_error(link->handle, last_fn, last_ret); + + failed: + orinoco_cs_release((u_long) link); +@@ -549,18 +544,13 @@ + dev_link_t *link = (dev_link_t *) arg; + struct net_device *dev = link->priv; + struct orinoco_private *priv = dev->priv; ++ unsigned long flags; + +- /* +- If the device is currently in use, we won't release until it +- is actually closed, because until then, we can't be sure that +- no one will try to access the device or its data structures. +- */ +- if (priv->open) { +- DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n", +- link->dev->dev_name); +- link->state |= DEV_STALE_CONFIG; +- return; +- } ++ /* We're committed to taking the device away now, so mark the ++ * hardware as unavailable */ ++ spin_lock_irqsave(&priv->lock, flags); ++ priv->hw_unavailable++; ++ spin_unlock_irqrestore(&priv->lock, flags); + + /* Don't bother checking to see if these succeed or not */ + CardServices(ReleaseConfiguration, link->handle); +@@ -593,14 +583,9 @@ + orinoco_lock(priv, &flags); + + netif_device_detach(dev); +- priv->hw_unavailable = 1; ++ priv->hw_unavailable++; + + orinoco_unlock(priv, &flags); +- +-/* if (link->open) */ +-/* orinoco_cs_stop(dev); */ +- +- mod_timer(&link->release, jiffies + HZ / 20); + } + break; + +@@ -619,13 +604,8 @@ + a better way, short of rewriting the PCMCIA + layer to not suck :-( */ + if (! test_bit(0, &card->hard_reset_in_progress)) { +- err = orinoco_lock(priv, &flags); +- if (err) { +- printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n", +- dev->name); +- break; +- } +- ++ spin_lock_irqsave(&priv->lock, flags); ++ + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: %s: Error %d downing interface\n", +@@ -634,9 +614,9 @@ + err); + + netif_device_detach(dev); +- priv->hw_unavailable = 1; +- +- orinoco_unlock(priv, &flags); ++ priv->hw_unavailable++; ++ ++ spin_unlock_irqrestore(&priv->lock, flags); + } + + CardServices(ReleaseConfiguration, link->handle); +@@ -653,10 +633,6 @@ + CardServices(RequestConfiguration, link->handle, + &link->conf); + +- /* If we're only getting these events because +- of the ResetCard in the hard reset, we +- don't need to do anything - orinoco_reset() +- will handle reinitialization. */ + if (! test_bit(0, &card->hard_reset_in_progress)) { + err = orinoco_reinit_firmware(dev); + if (err) { +@@ -668,9 +644,9 @@ + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); +- priv->hw_unavailable = 0; ++ priv->hw_unavailable--; + +- if (priv->open) { ++ if (priv->open && ! priv->hw_unavailable) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", +@@ -678,7 +654,7 @@ + + } + +- orinoco_unlock(priv, &flags); ++ spin_unlock_irqrestore(&priv->lock, flags); + } + } + break; +@@ -693,7 +669,7 @@ + + /* Can't be declared "const" or the whole __initdata section will + * become const */ +-static char version[] __initdata = "orinoco_cs.c 0.13b (David Gibson and others)"; ++static char version[] __initdata = "orinoco_cs.c 0.13e (David Gibson and others)"; + + static int __init + init_orinoco_cs(void) +@@ -722,7 +698,6 @@ + if (dev_list) + DEBUG(0, "orinoco_cs: Removing leftover devices.\n"); + while (dev_list != NULL) { +- del_timer(&dev_list->release); + if (dev_list->state & DEV_CONFIG) + orinoco_cs_release((u_long) dev_list); + orinoco_cs_detach(dev_list); +--- linux-2.4.21/drivers/pcmcia/pxa/Makefile~ramses-pcmcia ++++ linux-2.4.21/drivers/pcmcia/pxa/Makefile +@@ -12,6 +12,7 @@ + obj-$(CONFIG_ARCH_PXA_IDP) += pxa_idp.o + obj-$(CONFIG_ARCH_TRIZEPS2) += trizeps2.o + obj-$(CONFIG_ARCH_PXA_CERF) += ../sa1100_cerf.o ++obj-$(CONFIG_ARCH_RAMSES) += ramses.o + + obj-m := $(O_TARGET) + +--- linux-2.4.21/drivers/pcmcia/pxa/pxa.c~pxa-pcmcia ++++ linux-2.4.21/drivers/pcmcia/pxa/pxa.c +@@ -187,7 +187,6 @@ + struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK]; + struct pcmcia_state_array state_array; + unsigned int i, clock; +- unsigned long mecr; + + printk(KERN_INFO "Intel PXA250/210 PCMCIA (CS release %s)\n", CS_RELEASE); + +@@ -240,6 +239,8 @@ + pcmcia_low_level=&pxa_idp_pcmcia_ops; + } else if( machine_is_pxa_cerf()){ + pcmcia_low_level=&cerf_pcmcia_ops; ++ } else if( machine_is_ramses()){ ++ pcmcia_low_level=&ramses_pcmcia_ops; + } else if (machine_is_trizeps2()){ + #ifdef CONFIG_ARCH_TRIZEPS2 + pcmcia_low_level=&trizeps2_pcmcia_ops; +@@ -835,7 +836,7 @@ + static int pxa_pcmcia_set_io_map(unsigned int sock, + struct pccard_io_map *map){ + unsigned int clock, speed; +- unsigned long mecr, start; ++ unsigned long start; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + +@@ -941,7 +942,7 @@ + static int pxa_pcmcia_set_mem_map(unsigned int sock, + struct pccard_mem_map *map){ + unsigned int clock, speed; +- unsigned long mecr, start; ++ unsigned long start; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + +@@ -1076,7 +1077,6 @@ + char *p=buf; + unsigned int sock=(unsigned int)data; + unsigned int clock = get_lclk_frequency_10khz(); +- unsigned long mecr = MECR; + + p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", + pxa_pcmcia_socket[sock].k_state.detect?"detect ":"", +--- linux-2.4.21/drivers/pcmcia/pxa/pxa.h~ramses-pcmcia ++++ linux-2.4.21/drivers/pcmcia/pxa/pxa.h +@@ -228,6 +228,7 @@ + extern struct pcmcia_low_level lubbock_pcmcia_ops; + extern struct pcmcia_low_level pxa_idp_pcmcia_ops; + extern struct pcmcia_low_level cerf_pcmcia_ops; ++extern struct pcmcia_low_level ramses_pcmcia_ops; + extern struct pcmcia_low_level trizeps2_pcmcia_ops; + + #endif /* !defined(_PCMCIA_PXA_H) */ +--- /dev/null ++++ linux-2.4.21/drivers/pcmcia/pxa/ramses.c +@@ -0,0 +1,223 @@ ++/* ++ * linux/drivers/pcmcia/pxa/ramses.c ++ * ++ * 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) 2003 M&N Logistik-Lösungen Online GmbH ++ * ++ * Platform specific routines for the Ramses, based on those ++ * first done for the Lubbock and PXA IDP. ++ * ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++static int ++ramses_pcmcia_init(struct pcmcia_init *init) ++{ ++ int return_val = 0; ++ ++ /* Set PCMCIA Socket 0 power to standby mode. ++ * RAMSES has dedicated CPLD pins for all this stuff :-) ++ */ ++ ++ /* both slots disabled, reset NOT active */ ++ RAMSES_CPLD_PCCARD_EN = PCC0_ENABLE | PCC1_ENABLE; ++ ++ RAMSES_CPLD_PCCARD_PWR = 0; //all power to both slots off ++ //GPDR(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID)) &= ~GPIO_bit(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID)); ++ set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID), GPIO_BOTH_EDGES); ++ //GPDR(IRQ_TO_GPIO_2_80(CFCARD_RDYINT)) &= ~GPIO_bit(IRQ_TO_GPIO_2_80(CFCARD_RDYINT)); ++ set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(CFCARD_RDYINT), GPIO_FALLING_EDGE); ++ ++ return_val += ++ request_irq(CFCARD_CD_VALID, init->handler, SA_INTERRUPT, ++ "CF-Card CD", NULL); ++ ++ if (return_val < 0) { ++ return -1; ++ } ++ ++ return 2; ++} ++ ++static int ++ramses_pcmcia_shutdown(void) ++{ ++ ++ free_irq(CFCARD_CD_VALID, NULL); ++ ++ RAMSES_CPLD_PCCARD_EN = 0x03; //disable slots ++ udelay(200); ++ RAMSES_CPLD_PCCARD_PWR = 0; //shut off all power ++ ++ return 0; ++} ++ ++static int ++ramses_pcmcia_socket_state(struct pcmcia_state_array *state_array) ++{ ++ unsigned long status; ++ int return_val = 1; ++ int i; ++ volatile unsigned long *stat_regs[2] = { ++ &RAMSES_CPLD_PCCARD0_STATUS, ++ &RAMSES_CPLD_PCCARD1_STATUS ++ }; ++ ++ if (state_array->size < 2) ++ return -1; ++ ++ memset(state_array->state, 0, ++ (state_array->size) * sizeof (struct pcmcia_state)); ++ ++ for (i = 1; i < 2; i++) { ++ ++ status = *stat_regs[i]; ++ ++ /* this one is a gpio */ ++ state_array->state[i].detect = (PCC_DETECT(i)) ? 0 : 1; ++ ++ state_array->state[i].ready = ((status & _PCC_IRQ) == 0) ? 0 : 1; ++ state_array->state[i].bvd1 = (status & PCC_BVD1) ? 0 : 1; ++ state_array->state[i].bvd2 = (status & PCC_BVD2) ? 0 : 1; ++ state_array->state[i].wrprot = (status & _PCC_WRPROT) ? 1 : 0; ++ state_array->state[i].vs_3v = (status & PCC_VS1) ? 0 : 1; ++ state_array->state[i].vs_Xv = (status & PCC_VS2) ? 0 : 1; ++ } ++ ++ state_array->state[0].detect = 0; ++ state_array->state[0].ready = 0; ++ state_array->state[0].bvd1 = 0; ++ state_array->state[0].bvd2 = 0; ++ state_array->state[0].wrprot = 0; ++ state_array->state[0].vs_3v = 0; ++ state_array->state[0].vs_Xv = 0; ++ ++ return return_val; ++} ++ ++static int ++ramses_pcmcia_get_irq_info(struct pcmcia_irq_info *info) ++{ ++ switch (info->sock) { ++ case 0: ++ //info->irq = PCMCIA_S0_RDYINT; ++ //printk("//hs ramses_pcmcia_get_irq_info called for slot 0\n"); ++ break; ++ ++ case 1: ++ info->irq = CFCARD_RDYINT; ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++ramses_pcmcia_configure_socket(unsigned int sock, socket_state_t *state) ++{ ++ /* The Ramses uses the Maxim MAX1602, with the following connections: ++ * ++ * Socket 0 (PCMCIA): ++ * MAX1602 PXA_IDP Register ++ * Pin Signal RAMSES_CPLD_PCCARD_PWR: ++ * ----- ------- ---------------------- ++ * A0VPP PCC0_PWR0 bit0 ++ * A1VPP PCC0_PWR1 bit1 ++ * A0VCC PCC0_PWR2 bit2 ++ * A1VCC PCC0_PWR3 bit3 ++ * VX VCC ++ * VY +3.3V ++ * 12IN +12V ++ * CODE +3.3V Cirrus Code, CODE = High (VY) ++ * ++ * Socket 1 (PCMCIA): ++ * MAX1602 PXA_IDP Register ++ * Pin Signal RAMSES_CPLD_PCCARD_PWR: ++ * ----- ------- ---------------------- ++ * A0VPP PCC1_PWR0 bit4 ++ * A1VPP PCC1_PWR1 bit5 ++ * A0VCC PCC1_PWR2 bit6 ++ * A1VCC PCC1_PWR3 bit7 ++ * VX VCC ++ * VY +3.3V ++ * 12IN +12V ++ * CODE +3.3V Cirrus Code, CODE = High (VY) ++ * ++ */ ++ ++ if (sock == 1) { ++ ++ switch (state->Vcc) { ++ case 0: ++ RAMSES_CPLD_PCCARD_EN |= PCC1_ENABLE; // disable socket ++ udelay(200); ++ RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); ++ break; ++ ++ case 33: ++ RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); ++ RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR3; ++ RAMSES_CPLD_PCCARD_EN &= ~PCC1_ENABLE; //turn it on ++ break; ++ ++ case 50: ++ RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); ++ RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR2; ++ RAMSES_CPLD_PCCARD_EN &= ~PCC1_ENABLE; ++ break; ++ ++ default: ++ printk(KERN_ERR "%s(): unrecognized Vcc %u\n", ++ __FUNCTION__, state->Vcc); ++ return -1; ++ } ++ ++ switch (state->Vpp) { ++ case 0: ++ RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1); ++ break; ++ ++ case 120: ++ RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1); ++ RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR1; ++ break; ++ ++ default: ++ if (state->Vpp == state->Vcc) ++ RAMSES_CPLD_PCCARD_PWR = ++ (RAMSES_CPLD_PCCARD_PWR & ++ ~(PCC1_PWR0 | PCC1_PWR1)) | PCC1_PWR0; ++ else { ++ printk(KERN_ERR "%s(): unrecognized Vpp %u\n", ++ __FUNCTION__, state->Vpp); ++ return -1; ++ } ++ } ++ RAMSES_CPLD_PCCARD_EN = (state->flags & SS_RESET) ? (RAMSES_CPLD_PCCARD_EN | PCC1_RESET) ++ : (RAMSES_CPLD_PCCARD_EN & ~PCC1_RESET); ++ } ++ return 0; ++} ++ ++struct pcmcia_low_level ramses_pcmcia_ops = { ++ ramses_pcmcia_init, ++ ramses_pcmcia_shutdown, ++ ramses_pcmcia_socket_state, ++ ramses_pcmcia_get_irq_info, ++ ramses_pcmcia_configure_socket ++}; +--- linux-2.4.21/drivers/scsi/scsi.h~usb-sonycamera ++++ linux-2.4.21/drivers/scsi/scsi.h +@@ -610,6 +610,7 @@ + unsigned remap:1; /* support remapping */ + unsigned starved:1; /* unable to process commands because + host busy */ ++ unsigned no_start_on_add:1; /* do not issue start on add */ + + // Flag to allow revalidate to succeed in sd_open + int allow_revalidate; +--- linux-2.4.21/drivers/scsi/scsi_scan.c~usb-sonycamera ++++ linux-2.4.21/drivers/scsi/scsi_scan.c +@@ -37,6 +37,8 @@ + #define BLIST_ISDISK 0x100 /* Treat as (removable) disk */ + #define BLIST_ISROM 0x200 /* Treat as (removable) CD-ROM */ + #define BLIST_LARGELUN 0x400 /* LUNs larger than 7 despite reporting as SCSI 2 */ ++#define BLIST_NOSTARTONADD 0x1000 /* do not do automatic start on add */ ++ + + static void print_inquiry(unsigned char *data); + static int scan_scsis_single(unsigned int channel, unsigned int dev, +@@ -110,9 +112,10 @@ + {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ + {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ + {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ +- {"HP", "A6188A", "*", BLIST_SPARSELUN}, /* HP Va7100 Array */ +- {"HP", "A6189A", "*", BLIST_SPARSELUN}, /* HP Va7400 Array */ +- {"HP", "A6189B", "*", BLIST_SPARSELUN}, /* HP Va7410 Array */ ++ {"HP", "A6188A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7100 Array */ ++ {"HP", "A6189A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7400 Array */ ++ {"HP", "A6189B", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7110 Array */ ++ {"HP", "A6218A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7410 Array */ + {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ + {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 + * extra reset */ +@@ -145,7 +148,7 @@ + {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN}, + {"CANON", "IPUBJD", "*", BLIST_SPARSELUN}, + {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, +- {"DEC","HSG80","*", BLIST_FORCELUN}, ++ {"DEC","HSG80","*", BLIST_FORCELUN | BLIST_NOSTARTONADD}, + {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, + {"COMPAQ","CR3500","*", BLIST_FORCELUN}, + {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, +@@ -173,7 +176,11 @@ + {"HP", "NetRAID-4M", "*", BLIST_FORCELUN}, + {"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN}, + {"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN}, +- {"COMPAQ", "MSA1000", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"APPLE", "Xserve", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"COMPAQ", "MSA1000", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, ++ {"COMPAQ", "MSA1000 VOLUME", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, ++ {"COMPAQ", "HSV110", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, ++ {"HP", "HSV100", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, + {"HP", "C1557A", "*", BLIST_FORCELUN}, + {"IBM", "AuSaV1S2", "*", BLIST_FORCELUN}, + {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, +@@ -182,7 +189,8 @@ + {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, + {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, + {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, +- {"HITACHI", "OPEN-", "*", BLIST_SPARSELUN}, /* HITACHI XP Arrays */ ++ {"HITACHI", "OPEN-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HITACHI XP Arrays */ ++ {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HITACHI 9960 */ + {"WINSYS","FLASHDISK G6", "*", BLIST_SPARSELUN}, + {"DotHill","SANnet RAID X300", "*", BLIST_SPARSELUN}, + {"SUN", "T300", "*", BLIST_SPARSELUN}, +@@ -194,6 +202,12 @@ + {"SGI", "TP9400", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"SGI", "TP9500", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"MYLEX", "DACARMRB", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"PLATYPUS", "CX5", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"Raidtec", "FCR", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"HP", "C7200", "*", BLIST_SPARSELUN}, /* Medium Changer */ ++ {"SMSC", "USB 2 HS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"XYRATEX", "RS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, ++ {"NEC", "iStorage", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, + + /* + * Must be at end of list... +@@ -209,10 +223,14 @@ + static unsigned int max_scsi_luns = 1; + #endif + ++static unsigned int scsi_allow_ghost_devices = 0; ++ + #ifdef MODULE + + MODULE_PARM(max_scsi_luns, "i"); + MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 2^32-1)"); ++MODULE_PARM(scsi_allow_ghost_devices, "i"); ++MODULE_PARM_DESC(scsi_allow_ghost_devices, "allow devices marked as being offline to be accessed anyway (0 = off, else allow ghosts on lun 0 through scsi_allow_ghost_devices - 1"); + + #else + +@@ -232,6 +250,21 @@ + + __setup("max_scsi_luns=", scsi_luns_setup); + ++static int __init scsi_allow_ghost_devices_setup(char *str) ++{ ++ unsigned int tmp; ++ ++ if (get_option(&str, &tmp) == 1) { ++ scsi_allow_ghost_devices = tmp; ++ return 1; ++ } else { ++ printk("scsi_allow_ghost_devices_setup: usage scsi_allow_ghost_devices=n (0: off else\nallow ghost devices (ghost devices are devices that report themselves as\nbeing offline but which we allow access to anyway) on lun 0 through n - 1.\n"); ++ return 0; ++ } ++} ++ ++__setup("scsi_allow_ghost_devices=", scsi_allow_ghost_devices_setup); ++ + #endif + + static void print_inquiry(unsigned char *data) +@@ -608,6 +641,7 @@ + } else { + /* assume no peripheral if any other sort of error */ + scsi_release_request(SRpnt); ++ scsi_release_commandblocks(SDpnt); + return 0; + } + } +@@ -618,6 +652,24 @@ + */ + + /* ++ * If we are offline and we are on a LUN != 0, then skip this entry. ++ * If we are on a BLIST_FORCELUN device this will stop the scan at ++ * the first offline LUN (typically the correct thing to do). If ++ * we are on a BLIST_SPARSELUN device then this won't stop the scan, ++ * but it will keep us from having false entries in our device ++ * array. DL ++ * ++ * NOTE: Need to test this to make sure it doesn't cause problems ++ * with tape autoloaders, multidisc CD changers, and external ++ * RAID chassis that might use sparse luns or multiluns... DL ++ */ ++ if (lun != 0 && (scsi_result[0] >> 5) == 1) { ++ scsi_release_request(SRpnt); ++ scsi_release_commandblocks(SDpnt); ++ return 0; ++ } ++ ++ /* + * Get any flags for this device. + */ + bflags = get_device_flags (scsi_result); +@@ -655,8 +707,11 @@ + + SDpnt->removable = (0x80 & scsi_result[1]) >> 7; + /* Use the peripheral qualifier field to determine online/offline */ +- if (((scsi_result[0] >> 5) & 7) == 1) SDpnt->online = FALSE; +- else SDpnt->online = TRUE; ++ if ((((scsi_result[0] >> 5) & 7) == 1) && ++ (lun >= scsi_allow_ghost_devices)) ++ SDpnt->online = FALSE; ++ else ++ SDpnt->online = TRUE; + SDpnt->lockable = SDpnt->removable; + SDpnt->changed = 0; + SDpnt->access_count = 0; +@@ -742,6 +797,13 @@ + if ((bflags & BLIST_BORKEN) == 0) + SDpnt->borken = 0; + ++ /* ++ * Some devices may not want to have a start command automatically ++ * issued when a device is added. ++ */ ++ if (bflags & BLIST_NOSTARTONADD) ++ SDpnt->no_start_on_add = 1; ++ + /* + * If we want to only allow I/O to one of the luns attached to this device + * at a time, then we set this flag. +@@ -857,11 +919,26 @@ + * I think we need REPORT LUNS in future to avoid scanning + * of unused LUNs. But, that is another item. + */ ++ /* + if (*max_dev_lun < shpnt->max_lun) + *max_dev_lun = shpnt->max_lun; + else if ((max_scsi_luns >> 1) >= *max_dev_lun) + *max_dev_lun += shpnt->max_lun; + else *max_dev_lun = max_scsi_luns; ++ */ ++ /* ++ * Blech...the above code is broken. When you have a device ++ * that is present, and it is a FORCELUN device, then we ++ * need to scan *all* the luns on that device. Besides, ++ * skipping the scanning of LUNs is a false optimization. ++ * Scanning for a LUN on a present device is a very fast ++ * operation, it's scanning for devices that don't exist that ++ * is expensive and slow (although if you are truly scanning ++ * through MAX_SCSI_LUNS devices that would be bad, I hope ++ * all of the controllers out there set a reasonable value ++ * in shpnt->max_lun). DL ++ */ ++ *max_dev_lun = shpnt->max_lun; + return 1; + } + /* +--- linux-2.4.21/drivers/scsi/sd.c~usb-sonycamera ++++ linux-2.4.21/drivers/scsi/sd.c +@@ -775,7 +775,8 @@ + char nbuff[6]; + unsigned char *buffer; + unsigned long spintime_value = 0; +- int the_result, retries, spintime; ++ int retries, spintime; ++ unsigned int the_result; + int sector_size; + Scsi_Request *SRpnt; + +@@ -817,7 +818,7 @@ + do { + retries = 0; + +- while (retries < 3) { ++ do { + cmd[0] = TEST_UNIT_READY; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; +@@ -832,10 +833,10 @@ + + the_result = SRpnt->sr_result; + retries++; +- if (the_result == 0 +- || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION) +- break; +- } ++ } while (retries < 3 ++ && (the_result !=0 ++ || ((driver_byte(the_result) & DRIVER_SENSE) ++ && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION))); + + /* + * If the drive has indicated to us that it doesn't have +@@ -853,24 +854,47 @@ + break; + } + ++ if ((driver_byte(the_result) & DRIVER_SENSE) == 0) { ++ /* no sense, TUR either succeeded or failed ++ * with a status error */ ++ if(!spintime && the_result != 0) ++ printk(KERN_NOTICE "%s: Unit Not Ready, error = 0x%x\n", nbuff, the_result); ++ break; ++ } ++ ++ /* ++ * The device does not want the automatic start to be issued. ++ */ ++ if (rscsi_disks[i].device->no_start_on_add) { ++ break; ++ } ++ ++ /* ++ * If manual intervention is required, or this is an ++ * absent USB storage device, a spinup is meaningless. ++ */ ++ if (SRpnt->sr_sense_buffer[2] == NOT_READY && ++ SRpnt->sr_sense_buffer[12] == 4 /* not ready */ && ++ SRpnt->sr_sense_buffer[13] == 3) { ++ break; /* manual intervention required */ + /* Look for non-removable devices that return NOT_READY. + * Issue command to spin up drive for these cases. */ +- if (the_result && !rscsi_disks[i].device->removable && +- SRpnt->sr_sense_buffer[2] == NOT_READY) { ++ } else if (the_result && !rscsi_disks[i].device->removable && ++ SRpnt->sr_sense_buffer[2] == NOT_READY) { + unsigned long time1; + if (!spintime) { + printk("%s: Spinning up disk...", nbuff); + cmd[0] = START_STOP; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? +- ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; +- cmd[1] |= 1; /* Return immediately */ ++ ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; ++ cmd[1] |= 1; /* Return immediately */ + memset((void *) &cmd[2], 0, 8); +- cmd[4] = 1; /* Start spin cycle */ ++ cmd[4] = 1; /* Start spin cycle */ + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + +- SRpnt->sr_data_direction = SCSI_DATA_READ; ++ SRpnt->sr_data_direction = SCSI_DATA_NONE; + scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, + 0/*512*/, SD_TIMEOUT, MAX_RETRIES); + spintime_value = jiffies; +@@ -883,6 +907,14 @@ + time1 = schedule_timeout(time1); + } while(time1); + printk("."); ++ } else { ++ /* we don't understand the sense code, so it's ++ * probably pointless to loop */ ++ if(!spintime) { ++ printk(KERN_NOTICE "%s: Unit Not Ready, sense:\n", nbuff); ++ print_req_sense("", SRpnt); ++ } ++ break; + } + } while (the_result && spintime && + time_after(spintime_value + 100 * HZ, jiffies)); +--- linux-2.4.21/drivers/sound/ac97_codec.c~ucb1x00 ++++ linux-2.4.21/drivers/sound/ac97_codec.c +@@ -547,6 +547,12 @@ + val = SOUND_CAP_EXCL_INPUT; + break; + ++ case SOUND_MIXER_AC97: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ val = codec->codec_read(codec, val); ++ return put_user(val, (int *)arg); ++ + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + +@@ -575,6 +581,11 @@ + codec->recmask_io(codec, 0, val); + + return 0; ++ ++ case SOUND_MIXER_AC97: ++ codec->codec_write(codec, val >> 16 & 0xffff, val & 0xffff); ++ return 0; ++ + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + +--- linux-2.4.21/drivers/sound/pxa-ac97.c~pxa-ac97 ++++ linux-2.4.21/drivers/sound/pxa-ac97.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -164,6 +165,11 @@ + //pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); + pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); + pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); ++#if CONFIG_ARCH_RAMSES ++ pxa_ac97_codec.supported_mixers = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; ++ pxa_ac97_codec.stereo_mixers = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; ++ pxa_ac97_codec.record_sources = SOUND_MASK_MIC | SOUND_MASK_LINE; ++#endif + } + + pxa_ac97_refcount++; +@@ -198,7 +204,7 @@ + static int mixer_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) + { +- int ret, val; ++ int ret; + + ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); + if (ret) +@@ -282,6 +288,7 @@ + /* fall through */ + + case SOUND_PCM_READ_RATE: ++ val = 0; + if (file->f_mode & FMODE_READ) + val = codec_adc_rate; + if (file->f_mode & FMODE_WRITE) +@@ -342,6 +349,44 @@ + }; + + ++#ifdef CONFIG_PM ++ ++static int pxa_ac97_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ down(&pxa_ac97_mutex); ++ ++ switch (rqst) { ++ case PM_SUSPEND: ++ // TODO: set to low-power state? ++ GCR = GCR_ACLINK_OFF; ++ CKEN &= ~CKEN2_AC97; ++ break; ++ ++ case PM_RESUME: ++ CKEN |= CKEN2_AC97; ++ ++ GCR = 0; ++ udelay(10); ++ GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; ++ while (!(GSR & GSR_PCR)) { ++ schedule(); ++ } ++ ++ // need little hack for UCB1400 (should be moved elsewhere) ++ pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1); ++ pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); ++ pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); ++ break; ++ } ++ ++ up(&pxa_ac97_mutex); ++ ++ return 0; ++} ++ ++#endif ++ ++ + static int __init pxa_ac97_init(void) + { + int ret; +@@ -354,11 +399,18 @@ + ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); + pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); + ++#ifdef PM_DEBUG ++ ac97_audio_state.pmdev = pm_register(PM_SYS_UNKNOWN, 0x71783937, pxa_ac97_pm_callback, "pxa-ac97"); ++#else ++ ac97_audio_state.pmdev = pm_register(PM_SYS_UNKNOWN, 0x71783937, pxa_ac97_pm_callback); ++#endif ++ + return 0; + } + + static void __exit pxa_ac97_exit(void) + { ++ pm_unregister(ac97_audio_state.pmdev); + unregister_sound_dsp(ac97_audio_state.dev_dsp); + unregister_sound_mixer(pxa_ac97_codec.dev_mixer); + pxa_ac97_put(); +--- linux-2.4.21/drivers/sound/pxa-audio.h~pm ++++ linux-2.4.21/drivers/sound/pxa-audio.h +@@ -47,6 +47,9 @@ + int wr_ref:1; /* open reference for playback */ + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct semaphore sem; /* prevent races in attach/release */ ++#ifdef CONFIG_PM ++ struct pm_dev *pmdev; /* Power management */ ++#endif + } audio_state_t; + + extern int pxa_audio_attach(struct inode *inode, struct file *file, +--- linux-2.4.21/drivers/usb/Config.in~pxa-usb ++++ linux-2.4.21/drivers/usb/Config.in +@@ -5,7 +5,7 @@ + comment 'USB support' + + # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. +-if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" -o "$CONFIG_ARCH_PXA" = "y" ]; then + tristate 'Support for USB' CONFIG_USB + else + define_bool CONFIG_USB n +--- linux-2.4.21/drivers/usb/Makefile~usb-sl811 ++++ linux-2.4.21/drivers/usb/Makefile +@@ -80,6 +80,9 @@ + ifeq ($(CONFIG_USB_OHCI),y) + obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o + endif ++ ++subdir-$(CONFIG_USB_SL811HS_ALT)+= host ++ + subdir-$(CONFIG_USB_OHCI_AT91) += host + ifeq ($(CONFIG_USB_OHCI_AT91),y) + obj-y += host/usb-ohci.o +--- linux-2.4.21/drivers/usb/hcd.c~ramses-usb ++++ linux-2.4.21/drivers/usb/hcd.c +@@ -662,7 +662,9 @@ + pci_set_drvdata(dev, hcd); + hcd->driver = driver; + hcd->description = driver->description; ++#ifdef TODO + hcd->pdev = dev; ++#endif + printk (KERN_INFO "%s %s: %s\n", + hcd->description, dev->slot_name, dev->name); + +@@ -1201,6 +1203,7 @@ + return status; + + // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag ++#ifdef TODO + if (usb_pipecontrol (urb->pipe)) + urb->setup_dma = pci_map_single ( + #ifdef CONFIG_PCI +@@ -1223,7 +1226,7 @@ + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); +- ++#endif + if (urb->dev == hcd->bus->root_hub) + status = rh_urb_enqueue (hcd, urb); + else +@@ -1488,6 +1491,7 @@ + // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev) + + // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag ++#ifdef TODO + if (usb_pipecontrol (urb->pipe)) + pci_unmap_single ( + #ifdef CONFIG_PCI +@@ -1510,6 +1514,7 @@ + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); ++#endif + + /* pass ownership to the completion handler */ + urb->complete (urb); +--- linux-2.4.21/drivers/usb/host/Config.in~usb-sl811 ++++ linux-2.4.21/drivers/usb/host/Config.in +@@ -13,6 +13,9 @@ + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB + dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB ++if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" ]; then ++ dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL ++fi + if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + dep_tristate ' AT91RM9200 OHCI-compatible host interface support' CONFIG_USB_OHCI_AT91 $CONFIG_USB + fi +--- linux-2.4.21/drivers/usb/host/Makefile~usb-sl811 ++++ linux-2.4.21/drivers/usb/host/Makefile +@@ -10,6 +10,7 @@ + obj-$(CONFIG_USB_UHCI) += usb-uhci.o + obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o + obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o ++obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o + obj-$(CONFIG_USB_OHCI_AT91) += usb-ohci.o + + # Extract lists of the multi-part drivers. +--- /dev/null ++++ linux-2.4.21/drivers/usb/host/sl811.c +@@ -0,0 +1,2782 @@ ++/* ++ * SL811 Host Controller Interface driver for USB. ++ * ++ * Copyright (c) 2003/06, Courage Co., Ltd. ++ * ++ * Based on: ++ * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, ++ * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, ++ * Adam Richter, Gregory P. Smith; ++ * 2.Original SL811 driver (hc_sl811.o) by Pei Liu ++ * 3.Rewrited as sl811.o by Yin Aihua ++ * ++ * It's now support isochornous mode and more effective than hc_sl811.o ++ * Support x86 architecture now. ++ * ++ * 19.09.2003 (05.06.2003) HNE ++ * sl811_alloc_hc: Set "bus->bus_name" at init. ++ * sl811_reg_test (hc_reset,regTest): ++ * Stop output at first failed pattern. ++ * Down-Grade for Kernel 2.4.20 and from 2.4.22 ++ * Split hardware dependency into files sl811-x86.h and sl811-arm.h. ++ * ++ * 22.09.2003 HNE ++ * sl811_found_hc: First patterntest, than interrupt enable. ++ * Do nothing, if patterntest failed. Release IO if failed. ++ * Stop Interrupts first, than remove handle. (Old blocked Shared IRQ) ++ * Alternate IO-Base for second Controller (CF/USB1). ++ * ++ * 24.09.2003 HNE ++ * Remove all arm specific source (moved into include/asm/sl811-hw.h). ++ * ++ * 03.10.2003 HNE ++ * Low level only for port IO into hardware-include. ++ * ++ * To do: ++ * 1.Modify the timeout part, it's some messy ++ * 2.Use usb-a and usb-b set in Ping-Pong mode ++ * o Floppy do not work. ++ * o driver crash, if io region can't register ++ * o Only tested as module. Compiled-in version not tested! ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../hcd.h" ++#include "../hub.h" ++#include "sl811.h" ++ ++#define DRIVER_VERSION "v0.30" ++#define MODNAME "SL811" ++#define DRIVER_AUTHOR "Yin Aihua , Henry Nestler " ++#define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver" ++ ++static LIST_HEAD(sl811_hcd_list); ++ ++/* ++ * 0: normal prompt and information ++ * 1: error should not occur in normal ++ * 2: error maybe occur in normal ++ * 3: useful and detail debug information ++ * 4: function level enter and level inforamtion ++ * 5: endless information will output because of timer function or interrupt ++ */ ++static int debug = 0; ++MODULE_PARM(debug,"i"); ++MODULE_PARM_DESC(debug,"debug level"); ++ ++#include /* Include hardware and board depens */ ++ ++static void sl811_rh_int_timer_do(unsigned long ptr); ++static void sl811_transfer_done(struct sl811_hc *hc, int sof); ++ ++/* ++ * Read a byte of data from the SL811H/SL11H ++ */ ++static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset) ++{ ++ sl811_write_index (hc, offset); ++ return (sl811_read_data (hc)); ++} ++ ++/* ++ * Write a byte of data to the SL811H/SL11H ++ */ ++static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) ++{ ++ sl811_write_index_data (hc, offset, data); ++} ++ ++/* ++ * Read consecutive bytes of data from the SL811H/SL11H buffer ++ */ ++static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) ++{ ++ sl811_write_index (hc, offset); ++ while (size--) { ++ *buf++ = sl811_read_data(hc); ++ } ++} ++ ++/* ++ * Write consecutive bytes of data to the SL811H/SL11H buffer ++ */ ++static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) ++{ ++ sl811_write_index (hc, offset); ++ while (size--) { ++ sl811_write_data (hc, *buf); ++ buf++; ++ } ++} ++ ++/* ++ * This routine test the Read/Write functionality of SL811HS registers ++ */ ++static int sl811_reg_test(struct sl811_hc *hc) ++{ ++ int i, data, result = 0; ++ __u8 buf[256]; ++ ++ for (i = 0x10; i < 256; i++) { ++ /* save the original buffer */ ++ buf[i] = sl811_read(hc, i); ++ ++ /* Write the new data to the buffer */ ++ sl811_write(hc, i, ~i); ++ } ++ ++ /* compare the written data */ ++ for (i = 0x10; i < 256; i++) { ++ data = sl811_read(hc, i); ++ if (data != (__u8) ~i) { ++ PDEBUG(1, "reg %02x expected %02x got %02x", i, (__u8) ~i, data); ++ result = -1; ++ ++ /* If no Debug, show only first failed Address */ ++ if (!debug) ++ break; ++ } ++ } ++ ++ /* restore the data */ ++ for (i = 0x10; i < 256; i++) ++ sl811_write(hc, i, buf[i]); ++ ++ return result; ++} ++ ++/* ++ * Display all SL811HS register values ++ */ ++#if 0 /* unused (hne) */ ++static void sl811_reg_show(struct sl811_hc *hc) ++{ ++ int i; ++ ++ for (i = 0; i < 256; i++) ++ PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i)); ++} ++#endif ++ ++/* ++ * This function enables SL811HS interrupts ++ */ ++static void sl811_enable_interrupt(struct sl811_hc *hc) ++{ ++ PDEBUG(4, "enter"); ++ sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV); ++} ++ ++/* ++ * This function disables SL811HS interrupts ++ */ ++static void sl811_disable_interrupt(struct sl811_hc *hc) ++{ ++ PDEBUG(4, "enter"); ++ // Disable all other interrupt except for insert/remove. ++ sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); ++} ++ ++/* ++ * SL811 Virtual Root Hub ++ */ ++ ++/* Device descriptor */ ++static __u8 sl811_rh_dev_des[] = ++{ ++ 0x12, /* __u8 bLength; */ ++ 0x01, /* __u8 bDescriptorType; Device */ ++ 0x10, /* __u16 bcdUSB; v1.1 */ ++ 0x01, ++ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 bDeviceSubClass; */ ++ 0x00, /* __u8 bDeviceProtocol; */ ++ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ ++ 0x00, /* __u16 idVendor; */ ++ 0x00, ++ 0x00, /* __u16 idProduct; */ ++ 0x00, ++ 0x00, /* __u16 bcdDevice; */ ++ 0x00, ++ 0x00, /* __u8 iManufacturer; */ ++ 0x02, /* __u8 iProduct; */ ++ 0x01, /* __u8 iSerialNumber; */ ++ 0x01 /* __u8 bNumConfigurations; */ ++}; ++ ++/* Configuration descriptor */ ++static __u8 sl811_rh_config_des[] = ++{ ++ 0x09, /* __u8 bLength; */ ++ 0x02, /* __u8 bDescriptorType; Configuration */ ++ 0x19, /* __u16 wTotalLength; */ ++ 0x00, ++ 0x01, /* __u8 bNumInterfaces; */ ++ 0x01, /* __u8 bConfigurationValue; */ ++ 0x00, /* __u8 iConfiguration; */ ++ 0x40, /* __u8 bmAttributes; ++ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, ++ 4..0: resvd */ ++ 0x00, /* __u8 MaxPower; */ ++ ++ /* interface */ ++ 0x09, /* __u8 if_bLength; */ ++ 0x04, /* __u8 if_bDescriptorType; Interface */ ++ 0x00, /* __u8 if_bInterfaceNumber; */ ++ 0x00, /* __u8 if_bAlternateSetting; */ ++ 0x01, /* __u8 if_bNumEndpoints; */ ++ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 if_bInterfaceSubClass; */ ++ 0x00, /* __u8 if_bInterfaceProtocol; */ ++ 0x00, /* __u8 if_iInterface; */ ++ ++ /* endpoint */ ++ 0x07, /* __u8 ep_bLength; */ ++ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ ++ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ ++ 0x03, /* __u8 ep_bmAttributes; Interrupt */ ++ 0x08, /* __u16 ep_wMaxPacketSize; */ ++ 0x00, ++ 0xff /* __u8 ep_bInterval; 255 ms */ ++}; ++ ++/* root hub class descriptor*/ ++static __u8 sl811_rh_hub_des[] = ++{ ++ 0x09, /* __u8 bLength; */ ++ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ ++ 0x01, /* __u8 bNbrPorts; */ ++ 0x00, /* __u16 wHubCharacteristics; */ ++ 0x00, ++ 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ ++ 0x00, /* __u8 bHubContrCurrent; 0 mA */ ++ 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ ++ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ ++}; ++ ++/* ++ * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT. ++ */ ++static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len) ++{ ++ __u8 data = 0; ++ ++ PDEBUG(5, "enter"); ++ ++ /* ++ * Right now, It is assume the power is good and no changes and only one port. ++ */ ++ if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) { ++ data = 1<<1; ++ *(__u8 *)rh_change = data; ++ return 1; ++ } else ++ return 0; ++} ++ ++/* ++ * This function creates a timer that act as interrupt pipe in the virtual hub. ++ * ++ * Note: The virtual root hub's interrupt pipe are polled by the timer ++ * every "interval" ms ++ */ ++static void sl811_rh_init_int_timer(struct urb * urb) ++{ ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ hc->rh.interval = urb->interval; ++ ++ init_timer(&hc->rh.rh_int_timer); ++ hc->rh.rh_int_timer.function = sl811_rh_int_timer_do; ++ hc->rh.rh_int_timer.data = (unsigned long)urb; ++ hc->rh.rh_int_timer.expires = jiffies + ++ (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; ++ add_timer (&hc->rh.rh_int_timer); ++} ++ ++/* ++ * This function is called when the timer expires. It gets the the port ++ * change data and pass along to the upper protocol. ++ */ ++static void sl811_rh_int_timer_do(unsigned long ptr) ++{ ++ int len; ++ struct urb *urb = (struct urb *)ptr; ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ PDEBUG (5, "enter"); ++ ++ if(hc->rh.send) { ++ len = sl811_rh_send_irq(hc, urb->transfer_buffer, ++ urb->transfer_buffer_length); ++ if (len > 0) { ++ urb->actual_length = len; ++ if (urb->complete) ++ urb->complete(urb); ++ } ++ } ++ ++#ifdef SL811_TIMEOUT ++ ++{ ++ struct list_head *head, *tmp; ++ struct sl811_urb_priv *urbp; ++ struct urb *u; ++ int i; ++ static int timeout_count = 0; ++ ++// check time out every second ++ if (++timeout_count > 4) { ++ int max_scan = hc->active_urbs; ++ timeout_count = 0; ++ for (i = 0; i < 6; ++i) { ++ head = &hc->urb_list[i]; ++ tmp = head->next; ++ while (tmp != head && max_scan--) { ++ u = list_entry(tmp, struct urb, urb_list); ++ urbp = (struct sl811_urb_priv *)u->hcpriv; ++ tmp = tmp->next; ++ // Check if the URB timed out ++ if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) { ++ PDEBUG(3, "urb = %p time out, we kill it", urb); ++ u->transfer_flags |= USB_TIMEOUT_KILLED; ++ } ++ } ++ } ++ } ++} ++ ++#endif ++ // re-activate the timer ++ sl811_rh_init_int_timer(urb); ++} ++ ++/* helper macro */ ++#define OK(x) len = (x); break ++ ++/* ++ * This function handles all USB request to the the virtual root hub ++ */ ++static int sl811_rh_submit_urb(struct urb *urb) ++{ ++ struct usb_device *usb_dev = urb->dev; ++ struct sl811_hc *hc = usb_dev->bus->hcpriv; ++ struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet; ++ void *data = urb->transfer_buffer; ++ int buf_len = urb->transfer_buffer_length; ++ unsigned int pipe = urb->pipe; ++ __u8 data_buf[16]; ++ __u8 *bufp = data_buf; ++ int len = 0; ++ int status = 0; ++ ++ __u16 bmRType_bReq; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++ ++ if (usb_pipeint(pipe)) { ++ hc->rh.urb = urb; ++ hc->rh.send = 1; ++ hc->rh.interval = urb->interval; ++ sl811_rh_init_int_timer(urb); ++ urb->status = 0; ++ ++ return 0; ++ } ++ ++ bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); ++ wValue = le16_to_cpu (cmd->wValue); ++ wIndex = le16_to_cpu (cmd->wIndex); ++ wLength = le16_to_cpu (cmd->wLength); ++ ++ PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength); ++ ++ /* Request Destination: ++ without flags: Device, ++ USB_RECIP_INTERFACE: interface, ++ USB_RECIP_ENDPOINT: endpoint, ++ USB_TYPE_CLASS means HUB here, ++ USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here ++ */ ++ switch (bmRType_bReq) { ++ case RH_GET_STATUS: ++ *(__u16 *)bufp = cpu_to_le16(1); ++ OK(2); ++ ++ case RH_GET_STATUS | USB_RECIP_INTERFACE: ++ *(__u16 *)bufp = cpu_to_le16(0); ++ OK(2); ++ ++ case RH_GET_STATUS | USB_RECIP_ENDPOINT: ++ *(__u16 *)bufp = cpu_to_le16(0); ++ OK(2); ++ ++ case RH_GET_STATUS | USB_TYPE_CLASS: ++ *(__u32 *)bufp = cpu_to_le32(0); ++ OK(4); ++ ++ case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: ++ *(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus); ++ OK(4); ++ ++ case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: ++ switch (wValue) { ++ case 1: ++ OK(0); ++ } ++ break; ++ ++ case RH_CLEAR_FEATURE | USB_TYPE_CLASS: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ OK(0); ++ ++ case C_HUB_OVER_CURRENT: ++ OK(0); ++ } ++ break; ++ ++ case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; ++ OK(0); ++ ++ case USB_PORT_FEAT_SUSPEND: ++ hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; ++ OK(0); ++ ++ case USB_PORT_FEAT_POWER: ++ hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; ++ OK(0); ++ ++ case USB_PORT_FEAT_C_CONNECTION: ++ hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; ++ OK(0); ++ ++ case USB_PORT_FEAT_C_ENABLE: ++ hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; ++ OK(0); ++ ++ case USB_PORT_FEAT_C_SUSPEND: ++ hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; ++ OK(0); ++ ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; ++ OK(0); ++ ++ case USB_PORT_FEAT_C_RESET: ++ hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; ++ OK(0); ++ } ++ break; ++ ++ case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: ++ switch (wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; ++ OK(0); ++ ++ case USB_PORT_FEAT_RESET: ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET; ++ hc->rh_status.wPortChange = 0; ++ hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET; ++ hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; ++ OK(0); ++ ++ case USB_PORT_FEAT_POWER: ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER; ++ OK(0); ++ ++ case USB_PORT_FEAT_ENABLE: ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; ++ OK(0); ++ } ++ break; ++ ++ case RH_SET_ADDRESS: ++ hc->rh.devnum = wValue; ++ OK(0); ++ ++ case RH_GET_DESCRIPTOR: ++ switch ((wValue & 0xff00) >> 8) { ++ case USB_DT_DEVICE: ++ len = sizeof(sl811_rh_dev_des); ++ bufp = sl811_rh_dev_des; ++ OK(len); ++ ++ case USB_DT_CONFIG: ++ len = sizeof(sl811_rh_config_des); ++ bufp = sl811_rh_config_des; ++ OK(len); ++ ++ case USB_DT_STRING: ++ len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); ++ if (len > 0) { ++ bufp = data; ++ OK(len); ++ } ++ ++ default: ++ status = -EPIPE; ++ } ++ break; ++ ++ case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: ++ len = sizeof(sl811_rh_hub_des); ++ bufp = sl811_rh_hub_des; ++ OK(len); ++ ++ case RH_GET_CONFIGURATION: ++ bufp[0] = 0x01; ++ OK(1); ++ ++ case RH_SET_CONFIGURATION: ++ OK(0); ++ ++ default: ++ PDEBUG(1, "unsupported root hub command"); ++ status = -EPIPE; ++ } ++ ++ len = min(len, buf_len); ++ if (data != bufp) ++ memcpy(data, bufp, len); ++ urb->actual_length = len; ++ urb->status = status; ++ ++ PDEBUG(5, "len = %d, status = %d", len, status); ++ ++ urb->hcpriv = NULL; ++ urb->dev = NULL; ++ if (urb->complete) ++ urb->complete(urb); ++ ++ return 0; ++} ++ ++/* ++ * This function unlinks the URB ++ */ ++static int sl811_rh_unlink_urb(struct urb *urb) ++{ ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ ++ PDEBUG(5, "enter"); ++ ++ if (hc->rh.urb == urb) { ++ hc->rh.send = 0; ++ del_timer(&hc->rh.rh_int_timer); ++ hc->rh.urb = NULL; ++ urb->hcpriv = NULL; ++ usb_dec_dev_use(urb->dev); ++ urb->dev = NULL; ++ if (urb->transfer_flags & USB_ASYNC_UNLINK) { ++ urb->status = -ECONNRESET; ++ if (urb->complete) ++ urb->complete(urb); ++ } else ++ urb->status = -ENOENT; ++ } ++ ++ return 0; ++} ++ ++/* ++ * This function connect the virtual root hub to the USB stack ++ */ ++static int sl811_connect_rh(struct sl811_hc * hc) ++{ ++ struct usb_device *usb_dev; ++ ++ hc->rh.devnum = 0; ++ usb_dev = usb_alloc_dev(NULL, hc->bus); ++ if (!usb_dev) ++ return -ENOMEM; ++ ++ hc->bus->root_hub = usb_dev; ++ usb_connect(usb_dev); ++ ++ if (usb_new_device(usb_dev)) { ++ usb_free_dev(usb_dev); ++ return -ENODEV; ++ } ++ ++ PDEBUG(5, "leave success"); ++ ++ return 0; ++} ++ ++/* ++ * This function allocates private data space for the usb device ++ */ ++static int sl811_alloc_dev_priv(struct usb_device *usb_dev) ++{ ++ return 0; ++} ++ ++/* ++ * This function de-allocates private data space for the usb devic ++ */ ++static int sl811_free_dev_priv (struct usb_device *usb_dev) ++{ ++ return 0; ++} ++ ++/* ++ * This function allocates private data space for the urb ++ */ ++static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp; ++ ++ urbp = kmalloc(sizeof(*urbp), GFP_KERNEL); ++ if (!urbp) ++ return NULL; ++ ++ memset(urbp, 0, sizeof(*urbp)); ++ ++ INIT_LIST_HEAD(&urbp->td_list); ++ ++ urbp->urb = urb; ++ urb->hcpriv = urbp; ++ ++ return urbp; ++} ++ ++/* ++ * This function free private data space for the urb ++ */ ++static void sl811_free_urb_priv(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td; ++ struct list_head *head, *tmp; ++ ++ if (!urbp) ++ return ; ++ ++ head = &urbp->td_list; ++ tmp = head->next; ++ ++ while (tmp != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ tmp = tmp->next; ++ kfree(td); ++ } ++ ++ kfree(urbp); ++ urb->hcpriv = NULL; ++ ++ return ; ++} ++ ++/* ++ * This function calculate the bus time need by this td. ++ * Fix me! Can this use usb_calc_bus_time()? ++ */ ++static void sl811_calc_td_time(struct sl811_td *td) ++{ ++#if 1 ++ int time; ++ int len = td->len; ++ struct sl811_hc *hc = td->urb->dev->bus->hcpriv; ++ ++ if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) ++ time = 8*8*len + 1024; ++ else { ++ if (td->ctrl & SL811_USB_CTRL_PREAMBLE) ++ time = 8*8*len + 2048; ++ else ++ time = 8*len + 256; ++ } ++ ++ time += 2*10 * len; ++ ++ td->bustime = time; ++ ++#else ++ ++ unsigned long tmp; ++ int time; ++ int low_speed = usb_pipeslow(td->urb->pipe); ++ int input_dir = usb_pipein(td->urb->pipe); ++ int bytecount = td->len; ++ int isoc = usb_pipeisoc(td->urb->pipe); ++ ++ if (low_speed) { /* no isoc. here */ ++ if (input_dir) { ++ tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; ++ time = (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); ++ } else { ++ tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; ++ time = (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); ++ } ++ } else if (!isoc){ /* for full-speed: */ ++ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; ++ time = (9107L + BW_HOST_DELAY + tmp); ++ } else { /* for isoc: */ ++ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; ++ time = (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); ++ } ++ ++ td->bustime = time / 84; ++ ++#endif ++} ++ ++/* ++ * This function calculate the remainder bus time in current frame. ++ */ ++static inline int sl811_calc_bus_remainder(struct sl811_hc *hc) ++{ ++ return (sl811_read(hc, SL811_SOFCNTDIV) * 64); ++} ++ ++/* ++ * This function allocates td for the urb ++ */ ++static struct sl811_td* sl811_alloc_td(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td; ++ ++ td = kmalloc(sizeof (*td), GFP_KERNEL); ++ if (!td) ++ return NULL; ++ ++ memset(td, 0, sizeof(*td)); ++ ++ INIT_LIST_HEAD(&td->td_list); ++ ++ td->urb = urb; ++ list_add_tail(&td->td_list, &urbp->td_list); ++ ++ return td; ++} ++ ++/* ++ * Fill the td. ++ */ ++static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, __u8 pidep, __u8 dev, __u8 *buf) ++{ ++ td->ctrl = ctrl; ++ td->addr = addr; ++ td->len = len; ++ td->pidep = pidep; ++ td->dev = dev; ++ td->buf = buf; ++ td->left = len; ++ td->errcnt = 3; ++} ++ ++/* ++ * Fill the td. ++ */ ++static inline void sl811_reset_td(struct sl811_td *td) ++{ ++ td->status = 0; ++ td->left = td->len; ++ td->done = 0; ++ td->errcnt = 3; ++ td->nakcnt = 0; ++ td->td_status = 0; ++} ++ ++static void sl811_print_td(int level, struct sl811_td *td) ++{ ++ PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n " ++ "dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n " ++ "buf = %p, bustime = %d, td_status = %d\n", ++ td, td->ctrl, td->addr, td->len, td->pidep, ++ td->dev, td->status, td->left, td->errcnt, td->done, ++ td->buf, td->bustime, td->td_status); ++} ++ ++/* ++ * Isochronous transfers ++ */ ++static int sl811_submit_isochronous(struct urb *urb) ++{ ++ __u8 dev = usb_pipedevice(urb->pipe); ++ __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); ++ __u8 ctrl = 0; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ int i; ++ ++ PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp); ++ ++ /* Can't have low speed bulk transfers */ ++ if (usb_pipeslow(urb->pipe)) { ++ PDEBUG(1, "error, urb = %p, low speed device", urb); ++ return -EINVAL; ++ } ++ ++ if (usb_pipeout(urb->pipe)) ++ ctrl |= SL811_USB_CTRL_DIR_OUT; ++ ++ ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO; ++ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ urb->iso_frame_desc[i].actual_length = 0; ++ urb->iso_frame_desc[i].status = -EXDEV; ++ ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, ++ urb->iso_frame_desc[i].length, ++ pidep, dev, ++ urb->transfer_buffer + urb->iso_frame_desc[i].offset); ++ sl811_calc_td_time(td); ++ if (urbp->cur_td == NULL) ++ urbp->cur_td = urbp->first_td = td; ++ } ++ ++ urbp->last_td = td; ++ ++ PDEBUG(4, "leave success"); ++ ++/* ++// for debug ++ { ++ struct list_head *head, *tmp; ++ struct sl811_td *td; ++ int i = 0; ++ head = &urbp->td_list; ++ tmp = head->next; ++ ++ if (list_empty(&urbp->td_list)) { ++ PDEBUG(1, "bug!!! td list is empty!"); ++ return -ENODEV; ++ } ++ ++ while (tmp != head) { ++ ++i; ++ td = list_entry(tmp, struct sl811_td, td_list); ++ PDEBUG(2, "td = %p, i = %d", td, i); ++ tmp = tmp->next; ++ } ++ } ++*/ ++ return 0; ++} ++ ++/* ++ * Reset isochronous transfers ++ */ ++static void sl811_reset_isochronous(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ struct list_head *head, *tmp; ++ int i; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ urb->iso_frame_desc[i].actual_length = 0; ++ urb->iso_frame_desc[i].status = -EXDEV; ++ } ++ ++ head = &urbp->td_list; ++ tmp = head->next; ++ while (tmp != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ tmp = tmp->next; ++ sl811_reset_td(td); ++ } ++ ++ urbp->cur_td = urbp->first_td; ++ ++ urb->status = -EINPROGRESS; ++ urb->actual_length = 0; ++ urb->error_count = 0; ++} ++ ++/* ++ * Result the iso urb. ++ */ ++static void sl811_result_isochronous(struct urb *urb) ++{ ++ struct list_head *tmp, *head; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ int status = 0; ++ struct sl811_td *td; ++ int i; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ urb->actual_length = 0; ++ ++ i = 0; ++ head = &urbp->td_list; ++ tmp = head->next; ++ while (tmp != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ tmp = tmp->next; ++ ++ if (!td->done) { ++ if (urbp->unlink) ++ urb->status = -ENOENT; ++ else { ++ PDEBUG(1, "we should not get here!"); ++ urb->status = -EXDEV; ++ } ++ return ; ++ } ++ if (td->td_status) { ++ status = td->td_status; ++ urb->error_count++; ++ PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status); ++ } ++ ++ urb->iso_frame_desc[i].actual_length = td->len - td->left; ++ urb->actual_length += td->len - td->left; ++ urb->iso_frame_desc[i].status = td->td_status; ++ ++i; ++ if (td->left) ++ PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, td->len, td->left); ++ } ++ ++ urb->status = status; ++/* ++// for debug ++ PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, urb->status); ++*/ ++ PDEBUG(4, "leave success"); ++} ++ ++/* ++ * Interrupt transfers ++ */ ++static int sl811_submit_interrupt(struct urb *urb) ++{ ++ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); ++ int len = urb->transfer_buffer_length; ++ __u8 *data = urb->transfer_buffer; ++ __u8 dev = usb_pipedevice(urb->pipe); ++ __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); ++ __u8 ctrl = 0; ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ if (len > maxsze) { ++ PDEBUG(1, "length is big than max packet size, len = %d, max packet = %d", len, maxsze); ++ return -EINVAL; ++ } ++ if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) ++ ctrl |= SL811_USB_CTRL_PREAMBLE; ++ ++ ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; ++ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ++ ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data); ++ sl811_calc_td_time(td); ++ urbp->cur_td = urbp->first_td = urbp->last_td = td; ++ urbp->interval = 0; ++ ++ PDEBUG(4, "leave success"); ++ ++ return 0; ++} ++ ++/* ++ * Reset interrupt transfers ++ */ ++static void sl811_reset_interrupt(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = urbp->cur_td; ++ ++ PDEBUG(4, "enter, interval = %d", urb->interval); ++ ++ td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; ++ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ++ td->ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); ++ ++ sl811_reset_td(td); ++ ++ urbp->interval = urb->interval; ++ ++ urb->status = -EINPROGRESS; ++ urb->actual_length = 0; ++} ++ ++/* ++ * Result the interrupt urb. ++ */ ++static void sl811_result_interrupt(struct urb *urb) ++{ ++ struct list_head *tmp; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td; ++ int toggle; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ urb->actual_length = 0; ++ ++ tmp = &urbp->td_list; ++ tmp = tmp->next; ++ td = list_entry(tmp, struct sl811_td, td_list); ++ ++ // success. ++ if (td->done && td->td_status == 0) { ++ urb->actual_length += td->len - td->left; ++ urb->status = 0; ++ return ; ++ } ++ // tranfer is done but fail, reset the toggle. ++ else if (td->done && td->td_status) { ++ urb->status = td->td_status; ++reset_toggle: ++ toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); ++ PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); ++ return ; ++ } ++ // unlink, and not do transfer yet ++ else if (td->done == 0 && urbp->unlink && td->td_status == 0) { ++ urb->status = -ENOENT; ++ PDEBUG(3, "unlink and not transfer!"); ++ return ; ++ } ++ // unlink, and transfer not complete yet. ++ else if (td->done == 0 && urbp->unlink && td->td_status) { ++ urb->status = -ENOENT; ++ PDEBUG(3, "unlink and not complete!"); ++ goto reset_toggle; ++ } ++ // must be bug!!! ++ else {// (td->done == 0 && urbp->unlink == 0) ++ PDEBUG(1, "we should not get here!"); ++ urb->status = -EPIPE; ++ return ; ++ } ++} ++ ++/* ++ * Control transfers ++ */ ++static int sl811_submit_control(struct urb *urb) ++{ ++ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); ++ int len = urb->transfer_buffer_length; ++ __u8 *data = urb->transfer_buffer; ++ __u8 dev = usb_pipedevice(urb->pipe); ++ __u8 pidep = 0; ++ __u8 ctrl = 0; ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) ++ ctrl |= SL811_USB_CTRL_PREAMBLE; ++ ++ /* Build SETUP TD */ ++ pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe)); ++ ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT; ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet); ++ sl811_calc_td_time(td); ++ ++ urbp->cur_td = urbp->first_td = td; ++ ++ /* ++ * If direction is "send", change the frame from SETUP (0x2D) ++ * to OUT (0xE1). Else change it from SETUP to IN (0x69). ++ */ ++ pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); ++ if (usb_pipeout(urb->pipe)) ++ ctrl |= SL811_USB_CTRL_DIR_OUT; ++ else ++ ctrl &= ~SL811_USB_CTRL_DIR_OUT; ++ ++ /* Build the DATA TD's */ ++ while (len > 0) { ++ int pktsze = len; ++ ++ if (pktsze > maxsze) ++ pktsze = maxsze; ++ ++ /* Alternate Data0/1 (start with Data1) */ ++ ctrl ^= SL811_USB_CTRL_TOGGLE_1; ++ ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); ++ sl811_calc_td_time(td); ++ ++ data += pktsze; ++ len -= pktsze; ++ } ++ ++ /* Build the final TD for control status */ ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ /* It's IN if the pipe is an output pipe or we're not expecting data back */ ++ if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) { ++ pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe)); ++ ctrl &= ~SL811_USB_CTRL_DIR_OUT; ++ } else { ++ pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe)); ++ ctrl |= SL811_USB_CTRL_DIR_OUT; ++ } ++ ++ /* End in Data1 */ ++ ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); ++ sl811_calc_td_time(td); ++ urbp->last_td = td; ++/* ++// for debug ++ { ++ struct list_head *head, *tmp; ++ struct sl811_td *td; ++ int i = 0; ++ head = &urbp->td_list; ++ tmp = head->next; ++ ++ if (list_empty(&urbp->td_list)) { ++ PDEBUG(1, "bug!!! td list is empty!"); ++ return -ENODEV; ++ } ++ ++ while (tmp != head) { ++ ++i; ++ td = list_entry(tmp, struct sl811_td, td_list); ++ PDEBUG(3, "td = %p, i = %d", td, i); ++ tmp = tmp->next; ++ } ++ } ++*/ ++ PDEBUG(4, "leave success"); ++ ++ return 0; ++} ++ ++/* ++ * Result the control urb. ++ */ ++static void sl811_result_control(struct urb *urb) ++{ ++ struct list_head *tmp, *head; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ if (list_empty(&urbp->td_list)) { ++ PDEBUG(1, "td list is empty"); ++ return ; ++ } ++ ++ head = &urbp->td_list; ++ ++ tmp = head->next; ++ td = list_entry(tmp, struct sl811_td, td_list); ++ ++ /* The first TD is the SETUP phase, check the status, but skip the count */ ++ if (!td->done) { ++ PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done); ++ goto err_done; ++ } ++ if (td->td_status) { ++ PDEBUG(3, "setup phase error, td = %p, td status = %d", td, td->td_status); ++ goto err_status; ++ } ++ ++ urb->actual_length = 0; ++ ++ /* The rest of the TD's (but the last) are data */ ++ tmp = tmp->next; ++ while (tmp != head && tmp->next != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ tmp = tmp->next; ++ if (!td->done) { ++ PDEBUG(3, "data phase error, td = %p, done = %d", td, td->done); ++ goto err_done; ++ } ++ if (td->td_status) { ++ PDEBUG(3, "data phase error, td = %p, td status = %d", td, td->td_status); ++ goto err_status; ++ } ++ ++ urb->actual_length += td->len - td->left; ++ // short packet. ++ if (td->left) { ++ PDEBUG(3, "data phase short packet, td = %p, count = %d", td, td->len - td->left); ++ break; ++ } ++ } ++ ++ /* The last td is status phase */ ++ td = urbp->last_td; ++ if (!td->done) { ++ PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done); ++ goto err_done; ++ } ++ if (td->td_status) { ++ PDEBUG(3, "status phase error, td = %p, td status = %d", td, td->td_status); ++ goto err_status; ++ } ++ ++ PDEBUG(4, "leave success"); ++ ++ urb->status = 0; ++ return ; ++ ++err_done: ++ if (urbp->unlink) ++ urb->status = -ENOENT; ++ else { ++ PDEBUG(1, "we should not get here! td = %p", td); ++ urb->status = -EPIPE; ++ } ++ return ; ++ ++err_status: ++ urb->status = td->td_status; ++ return ; ++} ++ ++/* ++ * Bulk transfers ++ */ ++static int sl811_submit_bulk(struct urb *urb) ++{ ++ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); ++ int len = urb->transfer_buffer_length; ++ __u8 *data = urb->transfer_buffer; ++ __u8 dev = usb_pipedevice(urb->pipe); ++ __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); ++ __u8 ctrl = 0; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ if (len < 0) { ++ PDEBUG(1, "error, urb = %p, len = %d", urb, len); ++ return -EINVAL; ++ } ++ ++ /* Can't have low speed bulk transfers */ ++ if (usb_pipeslow(urb->pipe)) { ++ PDEBUG(1, "error, urb = %p, low speed device", urb); ++ return -EINVAL; ++ } ++ ++ if (usb_pipeout(urb->pipe)) ++ ctrl |= SL811_USB_CTRL_DIR_OUT; ++ ++ ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; ++ ++ /* Build the DATA TD's */ ++ do { /* Allow zero length packets */ ++ int pktsze = len; ++ ++ if (pktsze > maxsze) ++ pktsze = maxsze; ++ ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ /* Alternate Data0/1 (start with Data1) */ ++ ctrl &= ~SL811_USB_CTRL_TOGGLE_1; ++ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ++ ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); ++ sl811_calc_td_time(td); ++ ++ if (urbp->cur_td == NULL) ++ urbp->cur_td = urbp->first_td = td; ++ ++ data += pktsze; ++ len -= maxsze; ++ } while (len > 0); ++ ++ /* ++ * USB_ZERO_PACKET means adding a 0-length packet, if ++ * direction is OUT and the transfer_length was an ++ * exact multiple of maxsze, hence ++ * (len = transfer_length - N * maxsze) == 0 ++ * however, if transfer_length == 0, the zero packet ++ * was already prepared above. ++ */ ++ if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) && ++ !len && urb->transfer_buffer_length) { ++ ++ td = sl811_alloc_td(urb); ++ if (!td) ++ return -ENOMEM; ++ ++ /* Alternate Data0/1 (start with Data1) */ ++ ctrl &= ~SL811_USB_CTRL_TOGGLE_1; ++ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ++ ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); ++ ++ sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); ++ sl811_calc_td_time(td); ++ } ++ ++ urbp->last_td = td; ++ ++ PDEBUG(4, "leave success"); ++ ++ return 0; ++} ++ ++/* ++ * Reset bulk transfers ++ */ ++static int sl811_reset_bulk(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td; ++ struct list_head *head, *tmp; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ ++ head = &urbp->td_list; ++ tmp = head->next; ++ ++ while (tmp != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ ++ /* Alternate Data0/1 (start with Data1) */ ++ td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; ++ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ++ td->ctrl |= SL811_USB_CTRL_TOGGLE_1; ++ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); ++ ++ sl811_reset_td(td); ++ } ++ ++ urb->status = -EINPROGRESS; ++ urb->actual_length = 0; ++ urbp->cur_td = urbp->first_td; ++ ++ PDEBUG(4, "leave success"); ++ ++ return 0; ++} ++ ++/* ++ * Result the bulk urb. ++ */ ++static void sl811_result_bulk(struct urb *urb) ++{ ++ struct list_head *tmp, *head; ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = NULL; ++ int toggle; ++ ++ PDEBUG(4, "enter, urb = %p", urb); ++ ++ urb->actual_length = 0; ++ ++ head = &urbp->td_list; ++ tmp = head->next; ++ while (tmp != head) { ++ td = list_entry(tmp, struct sl811_td, td_list); ++ tmp = tmp->next; ++ ++ // success. ++ if (td->done && td->td_status == 0) { ++ urb->actual_length += td->len - td->left; ++ ++ // short packet ++ if (td->left) { ++ urb->status = 0; ++ PDEBUG(3, "short packet, td = %p, count = %d", td, td->len - td->left); ++ goto reset_toggle; ++ } ++ } ++ // tranfer is done but fail, reset the toggle. ++ else if (td->done && td->td_status) { ++ urb->status = td->td_status; ++ PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); ++ goto reset_toggle; ++ } ++ // unlink, and not do transfer yet ++ else if (td->done == 0 && urbp->unlink && td->td_status == 0) { ++ urb->status = -ENOENT; ++ PDEBUG(3, "unlink and not transfer!"); ++ return ; ++ } ++ // unlink, and transfer not complete yet. ++ else if (td->done == 0 && urbp->unlink && td->td_status) { ++ PDEBUG(3, "unlink and not complete!"); ++ urb->status = -ENOENT; ++ goto reset_toggle; ++ } ++ // must be bug!!! ++ else {// (td->done == 0 && urbp->unlink == 0) ++ urb->status = -EPIPE; ++ PDEBUG(1, "we should not get here!"); ++ return ; ++ } ++ } ++ ++ PDEBUG(4, "leave success"); ++ urb->status = 0; ++ return ; ++ ++reset_toggle: ++ toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); ++} ++ ++/* ++ * Find the first urb have the same dev and endpoint. ++ */ ++static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb) ++{ ++ struct list_head *tmp; ++ struct urb *u; ++ ++ if (!head || !urb) ++ return 0; ++ ++ tmp = head->next; ++ ++ while (tmp != head) { ++ u = list_entry(tmp, struct urb, urb_list); ++ if (u == urb) ++ return 1; ++ tmp = tmp->next; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Find the first urb have the same dev and endpoint. ++ */ ++static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb *urb) ++{ ++ struct list_head *tmp; ++ struct urb *u; ++ ++ if (!head || !urb) ++ return NULL; ++ ++ tmp = head->next; ++ ++ while (tmp != head) { ++ u = list_entry(tmp, struct urb, urb_list); ++ if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe))) ++ return u; ++ tmp = tmp->next; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * This function is called by the USB core API when an URB is available to ++ * process. ++ */ ++static int sl811_submit_urb(struct urb *urb) ++{ ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ unsigned int pipe = urb->pipe; ++ struct list_head *head = NULL; ++ unsigned long flags; ++ int bustime; ++ int ret = 0; ++ ++ if (!urb) { ++ PDEBUG(1, "urb is null"); ++ return -EINVAL; ++ } ++ ++ if (urb->hcpriv) { ++ PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv); ++ return -EINVAL; ++ } ++ ++ if (!urb->dev || !urb->dev->bus || !hc) { ++ PDEBUG(1, "dev or bus or hc is null"); ++ return -ENODEV; ++ } ++ ++ if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { ++ PDEBUG(2, "sl811_submit_urb: endpoint_halted"); ++ return -EPIPE; ++ } ++ ++ if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > SL811_DATA_LIMIT) { ++ printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", SL811_DATA_LIMIT); ++ return -EINVAL; ++ } ++ ++ /* a request to the virtual root hub */ ++ if (usb_pipedevice(pipe) == hc->rh.devnum) ++ return sl811_rh_submit_urb(urb); ++ ++ spin_lock_irqsave(&hc->hc_lock, flags); ++ spin_lock(&urb->lock); ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ head = &hc->iso_list; ++ break; ++ case PIPE_INTERRUPT: ++ head = &hc->intr_list; ++ break; ++ case PIPE_CONTROL: ++ head = &hc->ctrl_list; ++ break; ++ case PIPE_BULK: ++ head = &hc->bulk_list; ++ break; ++ } ++ ++ if (sl811_find_same_devep(head, urb)) { ++ list_add(&urb->urb_list, &hc->wait_list); ++ PDEBUG(4, "add to wait list"); ++ goto out_unlock; ++ } ++ ++ if (!sl811_alloc_urb_priv(urb)) { ++ ret = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ if (urb->number_of_packets <= 0) { ++ ret = -EINVAL; ++ break; ++ } ++ bustime = usb_check_bandwidth(urb->dev, urb); ++ if (bustime < 0) { ++ ret = bustime; ++ break; ++ } ++ if (!(ret = sl811_submit_isochronous(urb))) ++ usb_claim_bandwidth(urb->dev, urb, bustime, 1); ++ break; ++ case PIPE_INTERRUPT: ++ bustime = usb_check_bandwidth(urb->dev, urb); ++ if (bustime < 0) ++ ret = bustime; ++ else if (!(ret = sl811_submit_interrupt(urb))) ++ usb_claim_bandwidth(urb->dev, urb, bustime, 0); ++ break; ++ case PIPE_CONTROL: ++ ret = sl811_submit_control(urb); ++ break; ++ case PIPE_BULK: ++ ret = sl811_submit_bulk(urb); ++ break; ++ } ++ ++ if (!ret) { ++ ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; ++ list_add(&urb->urb_list, head); ++ PDEBUG(4, "add to type list"); ++ urb->status = -EINPROGRESS; ++ if (++hc->active_urbs == 1) ++ sl811_enable_interrupt(hc); ++ goto out_unlock; ++ } else { ++ PDEBUG(2, "submit urb fail! error = %d", ret); ++ sl811_free_urb_priv(urb); ++ } ++ ++out_unlock: ++ spin_unlock(&urb->lock); ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ ++ return ret; ++} ++ ++/* ++ * Submit the urb the wait list. ++ */ ++static int sl811_submit_urb_with_lock(struct urb *urb) ++{ ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ struct list_head *head = NULL; ++ int bustime; ++ int ret = 0; ++ ++ spin_lock(&urb->lock); ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ head = &hc->iso_list; ++ break; ++ case PIPE_INTERRUPT: ++ head = &hc->intr_list; ++ break; ++ case PIPE_CONTROL: ++ head = &hc->ctrl_list; ++ break; ++ case PIPE_BULK: ++ head = &hc->bulk_list; ++ break; ++ } ++ ++ if (!sl811_alloc_urb_priv(urb)) { ++ ret = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ if (urb->number_of_packets <= 0) { ++ ret = -EINVAL; ++ break; ++ } ++ bustime = usb_check_bandwidth(urb->dev, urb); ++ if (bustime < 0) { ++ ret = bustime; ++ break; ++ } ++ if (!(ret = sl811_submit_isochronous(urb))) ++ usb_claim_bandwidth(urb->dev, urb, bustime, 1); ++ break; ++ case PIPE_INTERRUPT: ++ bustime = usb_check_bandwidth(urb->dev, urb); ++ if (bustime < 0) ++ ret = bustime; ++ else if (!(ret = sl811_submit_interrupt(urb))) ++ usb_claim_bandwidth(urb->dev, urb, bustime, 0); ++ break; ++ case PIPE_CONTROL: ++ ret = sl811_submit_control(urb); ++ break; ++ case PIPE_BULK: ++ ret = sl811_submit_bulk(urb); ++ break; ++ } ++ ++ if (ret == 0) { ++ ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; ++ list_add(&urb->urb_list, head); ++ PDEBUG(4, "add to type list"); ++ urb->status = -EINPROGRESS; ++ if (++hc->active_urbs == 1) ++ sl811_enable_interrupt(hc); ++ goto out_unlock; ++ } else { ++ PDEBUG(2, "submit urb fail! error = %d", ret); ++ sl811_free_urb_priv(urb); ++ } ++ ++out_unlock: ++ spin_unlock(&urb->lock); ++ ++ return ret; ++} ++ ++/* ++ * Reset the urb ++ */ ++static void sl811_reset_urb(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ sl811_reset_isochronous(urb); ++ break; ++ case PIPE_INTERRUPT: ++ sl811_reset_interrupt(urb); ++ break; ++ case PIPE_CONTROL: ++ return; ++ case PIPE_BULK: ++ sl811_reset_bulk(urb); ++ break; ++ } ++ urbp->inserttime = jiffies; ++} ++ ++/* ++ * Return the result of a transfer ++ */ ++static void sl811_result_urb(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_hc *hc = urb->dev->bus->hcpriv; ++ struct list_head *head = NULL; ++ struct urb *u = NULL; ++ int reset = 0; ++ int ring = 0; ++ ++ if (urb->status != -EINPROGRESS) { ++ PDEBUG(1, "urb status is not EINPROGRESS!"); ++ return ; ++ } ++ ++ spin_lock(&urb->lock); ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ head = &hc->iso_list; ++ sl811_result_isochronous(urb); ++ ++ // if the urb is not unlink and is in a urb "ring", we reset it ++ if (!urbp->unlink && urb->next) ++ ring = 1; ++ break; ++ case PIPE_INTERRUPT: ++ head = &hc->intr_list; ++ sl811_result_interrupt(urb); ++ ++ // if the urb is not unlink and not "once" query, we reset. ++ if (!urbp->unlink && urb->interval) ++ reset = 1; ++ break; ++ case PIPE_CONTROL: ++ head = &hc->ctrl_list; ++ sl811_result_control(urb); ++ break; ++ case PIPE_BULK: ++ head = &hc->bulk_list; ++ sl811_result_bulk(urb); ++ ++ // if the urb is not unlink and is in a urb "ring", we reset it ++ if (!urbp->unlink && urb->next) ++ ring = 1; ++ break; ++ } ++ ++ PDEBUG(4, "result urb status = %d", urb->status); ++ ++ if (ring && urb->next == urb) ++ reset = 1; ++ ++ if (!reset) { ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ usb_release_bandwidth(urb->dev, urb, 1); ++ break; ++ case PIPE_INTERRUPT: ++ usb_release_bandwidth(urb->dev, urb, 0); ++ break; ++ } ++ sl811_free_urb_priv(urb); ++ } ++ ++ spin_unlock(&urb->lock); ++ ++ if (urb->complete) ++ urb->complete(urb); ++ ++ if (reset) { ++ spin_lock(&urb->lock); ++ sl811_reset_urb(urb); ++ if (usb_pipeint(urb->pipe)) ++ list_add(&urb->urb_list, &hc->idle_intr_list); ++ else ++ list_add(&urb->urb_list, head); ++ spin_unlock(&urb->lock); ++ } else { ++ if (--hc->active_urbs <= 0) { ++ hc->active_urbs = 0; ++ sl811_disable_interrupt(hc); ++ } ++ ++ if (ring) ++ u = urb->next; ++ else ++ u = sl811_find_same_devep(&hc->wait_list, urb); ++ ++ if (u) { ++ if (!list_empty(&u->urb_list)) ++ list_del(&u->urb_list); ++ if (sl811_submit_urb_with_lock(u)) ++ list_add(&u->urb_list, &hc->wait_list); ++ } ++ } ++} ++ ++ ++#ifdef SL811_TIMEOUT ++ ++/* ++ * Unlink the urb from the urb list ++ */ ++static int sl811_unlink_urb(struct urb *urb) ++{ ++ unsigned long flags; ++ struct sl811_hc *hc; ++ struct sl811_urb_priv *urbp; ++ int call = 0; ++ int schedule = 0; ++ int count = 0; ++ ++ if (!urb) { ++ PDEBUG(1, "urb is null"); ++ return -EINVAL; ++ } ++ ++ if (!urb->dev || !urb->dev->bus) { ++ PDEBUG(1, "dev or bus is null"); ++ return -ENODEV; ++ } ++ ++ hc = urb->dev->bus->hcpriv; ++ urbp = urb->hcpriv; ++ ++ /* a request to the virtual root hub */ ++ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) ++ return sl811_rh_unlink_urb(urb); ++ ++ spin_lock_irqsave(&hc->hc_lock, flags); ++ spin_lock(&urb->lock); ++ ++ // in wait list ++ if (sl811_find_same_urb(&hc->wait_list, urb)) { ++ PDEBUG(4, "unlink urb in wait list"); ++ list_del_init(&urb->urb_list); ++ urb->status = -ENOENT; ++ call = 1; ++ goto out; ++ } ++ ++ // in intr idle list. ++ if (sl811_find_same_urb(&hc->idle_intr_list, urb)) { ++ PDEBUG(4, "unlink urb in idle intr list"); ++ list_del_init(&urb->urb_list); ++ urb->status = -ENOENT; ++ sl811_free_urb_priv(urb); ++ usb_release_bandwidth(urb->dev, urb, 0); ++ if (--hc->active_urbs <= 0) { ++ hc->active_urbs = 0; ++ sl811_disable_interrupt(hc); ++ } ++ call = 1; ++ goto out; ++ } ++ ++ if (urb->status == -EINPROGRESS) { ++ PDEBUG(3, "urb is still in progress"); ++ urbp->unlink = 1; ++ ++re_unlink: ++ // Is it in progress? ++ urbp = urb->hcpriv; ++ if (urbp && hc->cur_td == urbp->cur_td) { ++ ++count; ++ if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { ++ PDEBUG(3, "unlink: cur td is still in progress! count = %d", count); ++re_schedule: ++ schedule = 1; ++ spin_unlock(&urb->lock); ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ schedule_timeout(HZ/50); ++ spin_lock_irqsave(&hc->hc_lock, flags); ++ spin_lock(&urb->lock); ++ } else { ++ PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count); ++ spin_unlock(&urb->lock); ++ sl811_transfer_done(hc, 0); ++ spin_lock(&urb->lock); ++ } ++ goto re_unlink; ++ } ++ ++ if (list_empty(&urb->urb_list)) { ++ PDEBUG(3, "unlink: list empty!"); ++ goto out; ++ } ++ ++ if (urb->transfer_flags & USB_TIMEOUT_KILLED) { ++ PDEBUG(3, "unlink: time out killed"); ++ // it is timeout killed by us ++ goto result; ++ } else if (urb->transfer_flags & USB_ASYNC_UNLINK) { ++ // we do nothing, just let it be processing later ++ PDEBUG(3, "unlink async, do nothing"); ++ goto out; ++ } else { ++ // synchron without callback ++ PDEBUG(3, "unlink synchron, we wait the urb complete or timeout"); ++ if (schedule == 0) { ++ PDEBUG(3, "goto re_schedule"); ++ goto re_schedule; ++ } else { ++ PDEBUG(3, "already scheduled"); ++ goto result; ++ } ++ } ++ } else if (!list_empty(&urb->urb_list)) { ++ PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); ++ //list_del_init(&urb->urb_list); ++ //call = 1; ++ } ++ ++out: ++ spin_unlock(&urb->lock); ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ ++ if (call && urb->complete) ++ urb->complete(urb); ++ ++ return 0; ++ ++result: ++ spin_unlock(&urb->lock); ++ ++ list_del_init(&urb->urb_list); ++ sl811_result_urb(urb); ++ ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ ++ return 0; ++} ++ ++#else ++ ++/* ++ * Unlink the urb from the urb list ++ */ ++static int sl811_unlink_urb(struct urb *urb) ++{ ++ unsigned long flags; ++ struct sl811_hc *hc; ++ struct sl811_urb_priv *urbp; ++ int call = 0; ++ ++ if (!urb) { ++ PDEBUG(1, "urb is null"); ++ return -EINVAL; ++ } ++ ++ if (!urb->dev || !urb->dev->bus) { ++ PDEBUG(1, "dev or bus is null"); ++ return -ENODEV; ++ } ++ ++ hc = urb->dev->bus->hcpriv; ++ urbp = urb->hcpriv; ++ ++ /* a request to the virtual root hub */ ++ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) ++ return sl811_rh_unlink_urb(urb); ++ ++ spin_lock_irqsave(&hc->hc_lock, flags); ++ spin_lock(&urb->lock); ++ ++ // in wait list ++ if (sl811_find_same_urb(&hc->wait_list, urb)) { ++ PDEBUG(2, "unlink urb in wait list"); ++ list_del_init(&urb->urb_list); ++ urb->status = -ENOENT; ++ call = 1; ++ goto out; ++ } ++ ++ if (urb->status == -EINPROGRESS) { ++ PDEBUG(2, "urb is still in progress"); ++ urbp->unlink = 1; ++ ++ // Is it in progress? ++ urbp = urb->hcpriv; ++ if (urbp && hc->cur_td == urbp->cur_td) { ++ // simple, let it out ++ PDEBUG(2, "unlink: cur td is still in progress!"); ++ hc->cur_td = NULL; ++ } ++ ++ goto result; ++ } else if (!list_empty(&urb->urb_list)) { ++ PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); ++ list_del_init(&urb->urb_list); ++ if (urbp) ++ goto result; ++ else ++ call = 1; ++ } ++ ++out: ++ spin_unlock(&urb->lock); ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ ++ if (call && urb->complete) ++ urb->complete(urb); ++ ++ return 0; ++ ++result: ++ spin_unlock(&urb->lock); ++ ++ list_del_init(&urb->urb_list); ++ sl811_result_urb(urb); ++ ++ spin_unlock_irqrestore(&hc->hc_lock, flags); ++ ++ return 0; ++} ++ ++#endif ++ ++static int sl811_get_current_frame_number(struct usb_device *usb_dev) ++{ ++ return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number; ++} ++ ++static struct usb_operations sl811_device_operations = ++{ ++ sl811_alloc_dev_priv, ++ sl811_free_dev_priv, ++ sl811_get_current_frame_number, ++ sl811_submit_urb, ++ sl811_unlink_urb ++}; ++ ++/* ++ * This functions transmit a td. ++ */ ++static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td) ++{ ++ sl811_print_td(4, td); ++ sl811_write_buf(hc, SL811_ADDR_A, &td->addr, 4); ++ if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT)) ++ sl811_write_buf(hc, td->addr, td->buf, td->len); ++ ++ sl811_write(hc, SL811_CTRL_A, td->ctrl); ++} ++ ++ ++/* ++ * This function checks the status of the transmitted or received packet ++ * and copy the data from the SL811HS register into a buffer. ++ */ ++static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td) ++{ ++ struct urb *urb = td->urb; ++#ifdef SL811_DEBUG ++ int dev = usb_pipedevice(td->urb->pipe); ++ int ep = usb_pipeendpoint(td->urb->pipe); ++#endif ++ ++ sl811_read_buf(hc, SL811_STS_A, &td->status, 2); ++ ++ if (td->status & SL811_USB_STS_ACK) { ++ td->done = 1; ++ ++/* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) { ++ PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep); ++ td->td_status = -EILSEQ; ++ } ++*/ ++ if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0) ++ sl811_read_buf(hc, td->addr, td->buf, td->len - td->left); ++ ++ if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) { ++ PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td); ++ td->td_status = -EREMOTEIO; ++ } else ++ td->td_status = 0; ++ } else if (td->status & SL811_USB_STS_STALL) { ++ PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td); ++ td->td_status = -EPIPE; ++ if (urb->dev) ++ usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe)); ++ td->done = 1; ++ } else if (td->status & SL811_USB_STS_OVERFLOW) { ++ PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT); ++ td->td_status = -EOVERFLOW; ++ td->done = 1; ++ } else if (td->status & SL811_USB_STS_TIMEOUT ) { ++ PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td); ++ td->td_status = -ETIMEDOUT; ++ if (--td->errcnt == 0) ++ td->done = 1; ++ } else if (td->status & SL811_USB_STS_ERROR) { ++ PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td); ++ td->td_status = -EILSEQ; ++ if (--td->errcnt == 0) ++ td->done = 1; ++ } else if (td->status & SL811_USB_STS_NAK) { ++ ++td->nakcnt; ++ PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt); ++ td->td_status = -EINPROGRESS; ++ if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) { ++ PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt); ++ td->td_status = -ETIMEDOUT; ++ td->done = 1; ++ } ++ } ++ ++ sl811_print_td(4, td); ++} ++ ++/* ++ * This function checks the status of current urb. ++ */ ++static int sl811_parse_cur_urb(struct urb *urb) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ struct sl811_td *td = urbp->cur_td; ++ struct list_head *tmp; ++ ++ sl811_print_td(5, td); ++ ++ // this td not done yet. ++ if (!td->done) ++ return 0; ++ ++ // the last ld, so the urb is done. ++ if (td == urbp->last_td) { ++ PDEBUG(4, "urb = %p is done success", td->urb); ++ if (usb_pipeisoc(td->urb->pipe)) ++ PDEBUG(4, "ISO URB DONE, td = %p", td); ++ return 1; ++ } ++ ++ // iso transfer, we always advance to next td ++ if (usb_pipeisoc(td->urb->pipe)) { ++ tmp = &td->td_list; ++ tmp = tmp->next; ++ urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); ++ PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td); ++ return 0; ++ } ++ ++ // some error occur, so the urb is done. ++ if (td->td_status) { ++ PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status); ++ return 1; ++ } ++ ++ // short packet. ++ if (td->left) { ++ if (usb_pipecontrol(td->urb->pipe)) { ++ // control packet, we advance to the last td ++ PDEBUG(3, "ctrl short packet, advance to last td"); ++ urbp->cur_td = urbp->last_td; ++ return 0; ++ } else { ++ // interrut and bulk packet, urb is over. ++ PDEBUG(3, "bulk or intr short packet, urb is over"); ++ return 1; ++ } ++ } ++ ++ // we advance to next td. ++ tmp = &td->td_list; ++ tmp = tmp->next; ++ urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); ++#ifdef SL811_DEBUG ++ PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td); ++ sl811_print_td(5, urbp->cur_td); ++ if (td == urbp->cur_td) ++ PDEBUG(1, "bug!!!"); ++#endif ++ return 0; ++} ++ ++/* ++ * Find the next td to transfer. ++ */ ++static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td) ++{ ++ struct sl811_urb_priv *urbp = urb->hcpriv; ++ ++ PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td); ++ ++ // iso don't schedule the td in the same frame. ++ if (usb_pipeisoc(cur_td->urb->pipe)) ++ return NULL; ++ ++ // cur td is not complete ++ if (!cur_td->done) ++ return NULL; ++ ++ // here, urbp->cur_td is already the next td; ++ return urbp->cur_td; ++} ++ ++/* ++ * Scan the list to find a active urb ++ */ ++static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next) ++{ ++ struct urb *urb; ++ int i; ++ ++ if (list_empty(next)) ++ return NULL; ++ ++ if (next == hc->cur_list) ++ return NULL; ++ ++ for (i = 0; i < 4; ++i) ++ if (next == &hc->urb_list[i]) ++ return NULL; ++ ++ urb = list_entry(next, struct urb, urb_list); ++ PDEBUG(4, "next urb in list is at %p", urb); ++ ++ return urb; ++} ++ ++/* ++ * Find the next td to transfer. ++ */ ++static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next) ++{ ++ struct urb *urb = NULL; ++ int back_loop = 1; ++ struct list_head *old_list = hc->cur_list; ++ ++ // try to get next urb in the same list. ++ if (next) { ++ urb = sl811_get_list_next_urb(hc, next); ++ if (!urb) ++ ++hc->cur_list; ++ } ++ ++ // try other list. ++ if (!urb) { ++re_loop: ++ // try all the list. ++ while (hc->cur_list < &hc->urb_list[4]) { ++ if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next))) ++ return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td; ++ ++hc->cur_list; ++ } ++ // the last list is try ++ if (back_loop && (old_list >= &hc->ctrl_list)) { ++ hc->cur_list = &hc->ctrl_list; ++ back_loop = 0; ++ goto re_loop; ++ } ++ } ++ ++ if (hc->cur_list > &hc->urb_list[3]) ++ hc->cur_list = &hc->ctrl_list; ++ ++ return NULL; ++} ++ ++/* ++ * This function process the transfer rusult. ++ */ ++static void sl811_transfer_done(struct sl811_hc *hc, int sof) ++{ ++ struct sl811_td *cur_td = hc->cur_td, *next_td = NULL; ++ struct urb *cur_urb = NULL; ++ struct list_head *next = NULL; ++ int done; ++ ++ PDEBUG(5, "enter"); ++ ++ if (cur_td == NULL) { ++ PDEBUG(1, "in done interrupt, but td is null, be already parsed?"); ++ return ; ++ } ++ ++ cur_urb = cur_td->urb; ++ hc->cur_td = NULL; ++ next = &cur_urb->urb_list; ++ next = next->next; ++ ++ spin_lock(&cur_urb->lock); ++ sl811_parse_cur_td(hc, cur_td); ++ done = sl811_parse_cur_urb(cur_urb); ++ spin_unlock(&cur_urb->lock); ++ ++ if (done) { ++ list_del_init(&cur_urb->urb_list); ++ cur_td = NULL; ++ sl811_result_urb(cur_urb); ++ } ++ ++ if (sof) ++ return ; ++ ++ if (!done) { ++ next_td = sl811_schedule_next_td(cur_urb, cur_td); ++ if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) { ++ hc->cur_td = next_td; ++ PDEBUG(5, "ADD TD"); ++ sl811_trans_cur_td(hc, next_td); ++ return ; ++ } ++ } ++ ++ while (1) { ++ next_td = sl811_schedule_next_urb(hc, next); ++ if (!next_td) ++ return; ++ if (next_td == cur_td) ++ return; ++ next = &next_td->urb->urb_list; ++ next = next->next; ++ if (sl811_calc_bus_remainder(hc) > next_td->bustime) { ++ hc->cur_td = next_td; ++ PDEBUG(5, "ADD TD"); ++ sl811_trans_cur_td(hc, next_td); ++ return ; ++ } ++ } ++} ++ ++/* ++ * ++ */ ++static void inline sl811_dec_intr_interval(struct sl811_hc *hc) ++{ ++ struct list_head *head, *tmp; ++ struct urb *urb; ++ struct sl811_urb_priv *urbp; ++ ++ if (list_empty(&hc->idle_intr_list)) ++ return ; ++ ++ head = &hc->idle_intr_list; ++ tmp = head->next; ++ ++ while (tmp != head) { ++ urb = list_entry(tmp, struct urb, urb_list); ++ tmp = tmp->next; ++ spin_lock(&urb->lock); ++ urbp = urb->hcpriv; ++ if (--urbp->interval == 0) { ++ list_del(&urb->urb_list); ++ list_add(&urb->urb_list, &hc->intr_list); ++ PDEBUG(4, "intr urb active"); ++ } ++ spin_unlock(&urb->lock); ++ } ++} ++ ++/* ++ * The sof interrupt is happen. ++ */ ++static void sl811_start_sof(struct sl811_hc *hc) ++{ ++ struct sl811_td *next_td; ++#ifdef SL811_DEBUG ++ static struct sl811_td *repeat_td = NULL; ++ static int repeat_cnt = 1; ++#endif ++ if (++hc->frame_number > 1024) ++ hc->frame_number = 0; ++ ++ if (hc->active_urbs == 0) ++ return ; ++ ++ sl811_dec_intr_interval(hc); ++ ++ if (hc->cur_td) { ++ if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { ++#ifdef SL811_DEBUG ++ if (repeat_td == hc->cur_td) ++ ++repeat_cnt; ++ else { ++ if (repeat_cnt >= 2) ++ PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt); ++ repeat_cnt = 1; ++ repeat_td = hc->cur_td; ++ } ++#endif ++ return ; ++ } else { ++ PDEBUG(2, "lost of interrupt in sof? do parse!"); ++ sl811_transfer_done(hc, 1); ++ ++ // let this frame idle ++ return; ++ } ++ } ++ ++ hc->cur_list = &hc->iso_list; ++ ++ if (hc->active_urbs == 0) ++ return ; ++ ++ next_td = sl811_schedule_next_urb(hc, NULL); ++ if (!next_td) { ++#ifdef SL811_DEBUG ++ if (list_empty(&hc->idle_intr_list)) ++ PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs); ++#endif ++ return; ++ } ++ if (sl811_calc_bus_remainder(hc) > next_td->bustime) { ++ hc->cur_td = next_td; ++ sl811_trans_cur_td(hc, next_td); ++ } else ++ PDEBUG(2, "bus time if not enough, why?"); ++} ++ ++/* ++ * This function resets SL811HS controller and detects the speed of ++ * the connecting device ++ * ++ * Return: 0 = no device attached; 1 = USB device attached ++ */ ++static int sl811_hc_reset(struct sl811_hc *hc) ++{ ++ int status ; ++ ++ sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); ++ sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); ++ ++ mdelay(20); ++ ++ // Disable hardware SOF generation, clear all irq status. ++ sl811_write(hc, SL811_CTRL1, 0); ++ mdelay(2); ++ sl811_write(hc, SL811_INTRSTS, 0xff); ++ status = sl811_read(hc, SL811_INTRSTS); ++ ++ if (status & SL811_INTR_NOTPRESENT) { ++ // Device is not present ++ PDEBUG(0, "Device not present"); ++ hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); ++ hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; ++ sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); ++ return 0; ++ } ++ ++ // Send SOF to address 0, endpoint 0. ++ sl811_write(hc, SL811_LEN_B, 0); ++ sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); ++ sl811_write(hc, SL811_DEV_B, 0x00); ++ sl811_write (hc, SL811_SOFLOW, SL811_12M_HI); ++ ++ if (status & SL811_INTR_SPEED_FULL) { ++ /* full speed device connect directly to root hub */ ++ PDEBUG (0, "Full speed Device attached"); ++ ++ sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); ++ mdelay(20); ++ sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); ++ sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF); ++ ++ /* start the SOF or EOP */ ++ sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; ++ hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; ++ mdelay(2); ++ sl811_write (hc, SL811_INTRSTS, 0xff); ++ } else { ++ /* slow speed device connect directly to root-hub */ ++ PDEBUG(0, "Low speed Device attached"); ++ ++ sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); ++ mdelay(20); ++ sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); ++ sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); ++ ++ /* start the SOF or EOP */ ++ sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); ++ hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; ++ mdelay(2); ++ sl811_write(hc, SL811_INTRSTS, 0xff); ++ } ++ ++ hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; ++ sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); ++ ++ return 1; ++} ++ ++/* ++ * Interrupt service routine. ++ */ ++static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r) ++{ ++ __u8 status; ++ struct sl811_hc *hc = __hc; ++ ++ status = sl811_read(hc, SL811_INTRSTS); ++ if (status == 0) ++ return ; /* Not me */ ++ ++ sl811_write(hc, SL811_INTRSTS, 0xff); ++ ++ if (status & SL811_INTR_INSRMV) { ++ sl811_write(hc, SL811_INTR, 0); ++ sl811_write(hc, SL811_CTRL1, 0); ++ // wait for device stable ++ mdelay(100); ++ sl811_hc_reset(hc); ++ return ; ++ } ++ ++ spin_lock(&hc->hc_lock); ++ ++ if (status & SL811_INTR_DONE_A) { ++ if (status & SL811_INTR_SOF) { ++ sl811_transfer_done(hc, 1); ++ PDEBUG(4, "sof in done!"); ++ sl811_start_sof(hc); ++ } else ++ sl811_transfer_done(hc, 0); ++ } else if (status & SL811_INTR_SOF) ++ sl811_start_sof(hc); ++ ++ spin_unlock(&hc->hc_lock); ++ ++ return ; ++} ++ ++/* ++ * This function allocates all data structure and store in the ++ * private data structure. ++ * ++ * Return value : data structure for the host controller ++ */ ++static struct sl811_hc* __devinit sl811_alloc_hc(void) ++{ ++ struct sl811_hc *hc; ++ struct usb_bus *bus; ++ int i; ++ ++ PDEBUG(4, "enter"); ++ ++ hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL); ++ if (!hc) ++ return NULL; ++ ++ memset(hc, 0, sizeof(struct sl811_hc)); ++ ++ hc->rh_status.wPortStatus = USB_PORT_STAT_POWER; ++ hc->rh_status.wPortChange = 0; ++ ++ hc->active_urbs = 0; ++ INIT_LIST_HEAD(&hc->hc_hcd_list); ++ list_add(&hc->hc_hcd_list, &sl811_hcd_list); ++ ++ init_waitqueue_head(&hc->waitq); ++ ++ for (i = 0; i < 6; ++i) ++ INIT_LIST_HEAD(&hc->urb_list[i]); ++ ++ hc->cur_list = &hc->iso_list; ++ ++ bus = usb_alloc_bus(&sl811_device_operations); ++ if (!bus) { ++ kfree (hc); ++ return NULL; ++ } ++ ++ hc->bus = bus; ++ bus->bus_name = MODNAME; ++ bus->hcpriv = hc; ++ ++ return hc; ++} ++ ++/* ++ * This function De-allocate all resources ++ */ ++static void sl811_release_hc(struct sl811_hc *hc) ++{ ++ PDEBUG(4, "enter"); ++ ++ /* disconnect all devices */ ++ if (hc->bus->root_hub) ++ usb_disconnect(&hc->bus->root_hub); ++ ++ // Stop interrupt handle ++ if (hc->irq) ++ free_irq(hc->irq, hc); ++ hc->irq = 0; ++ ++ /* Stop interrupt for sharing */ ++ if (hc->addr_io) { ++ /* Disable Interrupts */ ++ sl811_write(hc, SL811_INTR, 0); ++ ++ /* Remove all Interrupt events */ ++ mdelay(2); ++ sl811_write(hc, SL811_INTRSTS, 0xff); ++ } ++ ++ /* free io regions */ ++ sl811_release_regions(hc); ++ ++ usb_deregister_bus(hc->bus); ++ usb_free_bus(hc->bus); ++ ++ list_del(&hc->hc_hcd_list); ++ INIT_LIST_HEAD(&hc->hc_hcd_list); ++ ++ kfree (hc); ++} ++ ++/* ++ * This function request IO memory regions, request IRQ, and ++ * allocate all other resources. ++ * ++ * Input: addr_io = first IO address ++ * data_io = second IO address ++ * irq = interrupt number ++ * ++ * Return: 0 = success or error condition ++ */ ++static int __devinit sl811_found_hc(int addr_io, int data_io, int irq) ++{ ++ struct sl811_hc *hc; ++ ++ PDEBUG(4, "enter"); ++ ++ hc = sl811_alloc_hc(); ++ if (!hc) ++ return -ENOMEM; ++ ++ if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { ++ PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); ++ sl811_release_hc(hc); ++ return -EBUSY; ++ } ++ ++ if (sl811_reg_test(hc)) { ++ PDEBUG(1, "SL811 register test failed!"); ++ sl811_release_hc(hc); ++ return -ENODEV; ++ } ++ ++//#ifdef SL811_DEBUG_VERBOSE ++ { ++ __u8 u = sl811_read(hc, SL811_HWREV); ++ ++ // Show the hardware revision of chip ++ PDEBUG(1, "SL811 HW: %02Xh", u); ++ switch (u & 0xF0) { ++ case 0x00: PDEBUG(1, "SL11H"); break; ++ case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; ++ case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; ++ default: PDEBUG(1, "Revision unknown!"); ++ } ++ } ++//#endif // SL811_DEBUG_VERBOSE ++ ++ sl811_init_irq(); ++ ++ usb_register_bus(hc->bus); ++ ++ if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) { ++ PDEBUG(1, "request interrupt %d failed", irq); ++ sl811_release_hc(hc); ++ return -EBUSY; ++ } ++ hc->irq = irq; ++ ++ printk(KERN_INFO __FILE__ ": USB SL811 at %08x,%08x, IRQ %d\n", ++ hc->addr_io, hc->data_io, irq); ++ ++ sl811_hc_reset(hc); ++ sl811_connect_rh(hc); ++ ++ return 0; ++} ++ ++/* ++ * This is an init function, and it is the first function being called ++ * ++ * Return: 0 = success or error condition ++ */ ++static int __init sl811_hcd_init(void) ++{ ++ int ret = -ENODEV; ++ ++ PDEBUG(4, "enter"); ++ ++ info(DRIVER_VERSION " : " DRIVER_DESC); ++ ++#ifdef CONFIG_X86 ++ { ++ int count; ++ // registering some instance ++ for (count = 0; count < MAX_CONTROLERS; count++) { ++ if (io[count]) { ++ ret = sl811_found_hc(io[count], io[count]+OFFSET_DATA_REG, irq[count]); ++ if (ret) ++ return (ret); ++ } ++ } ++ } ++#endif ++#ifdef CONFIG_ARCH_RAMSES ++ ret = sl811_found_hc(0,0,SL811HS_IRQ); ++#endif ++ ++ return ret; ++} ++ ++/* ++ * This is a cleanup function, and it is called when module is unloaded. ++ */ ++static void __exit sl811_hcd_cleanup(void) ++{ ++ struct list_head *list = sl811_hcd_list.next; ++ struct sl811_hc *hc; ++ ++ PDEBUG(4, "enter"); ++ ++ for (; list != &sl811_hcd_list; ) { ++ hc = list_entry(list, struct sl811_hc, hc_hcd_list); ++ list = list->next; ++ sl811_release_hc(hc); ++ } ++} ++ ++module_init(sl811_hcd_init); ++module_exit(sl811_hcd_cleanup); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); +--- /dev/null ++++ linux-2.4.21/drivers/usb/host/sl811.h +@@ -0,0 +1,177 @@ ++#ifndef __LINUX_SL811_H ++#define __LINUX_SL811_H ++ ++#define SL811_DEBUG ++ ++#ifdef SL811_DEBUG ++ #define PDEBUG(level, fmt, args...) \ ++ if (debug >= (level)) info("[%s:%d] " fmt, \ ++ __PRETTY_FUNCTION__, __LINE__ , ## args) ++#else ++ #define PDEBUG(level, fmt, args...) do {} while(0) ++#endif ++ ++//#define SL811_TIMEOUT ++ ++/* Sl811 host control register */ ++#define SL811_CTRL_A 0x00 ++#define SL811_ADDR_A 0x01 ++#define SL811_LEN_A 0x02 ++#define SL811_STS_A 0x03 /* read */ ++#define SL811_PIDEP_A 0x03 /* write */ ++#define SL811_CNT_A 0x04 /* read */ ++#define SL811_DEV_A 0x04 /* write */ ++#define SL811_CTRL1 0x05 ++#define SL811_INTR 0x06 ++#define SL811_CTRL_B 0x08 ++#define SL811_ADDR_B 0x09 ++#define SL811_LEN_B 0x0A ++#define SL811_STS_B 0x0B /* read */ ++#define SL811_PIDEP_B 0x0B /* write */ ++#define SL811_CNT_B 0x0C /* read */ ++#define SL811_DEV_B 0x0C /* write */ ++#define SL811_INTRSTS 0x0D /* write clears bitwise */ ++#define SL811_HWREV 0x0E /* read */ ++#define SL811_SOFLOW 0x0E /* write */ ++#define SL811_SOFCNTDIV 0x0F /* read */ ++#define SL811_CTRL2 0x0F /* write */ ++ ++/* USB control register bits (addr 0x00 and addr 0x08) */ ++#define SL811_USB_CTRL_ARM 0x01 ++#define SL811_USB_CTRL_ENABLE 0x02 ++#define SL811_USB_CTRL_DIR_OUT 0x04 ++#define SL811_USB_CTRL_ISO 0x10 ++#define SL811_USB_CTRL_SOF 0x20 ++#define SL811_USB_CTRL_TOGGLE_1 0x40 ++#define SL811_USB_CTRL_PREAMBLE 0x80 ++ ++/* USB status register bits (addr 0x03 and addr 0x0B) */ ++#define SL811_USB_STS_ACK 0x01 ++#define SL811_USB_STS_ERROR 0x02 ++#define SL811_USB_STS_TIMEOUT 0x04 ++#define SL811_USB_STS_TOGGLE_1 0x08 ++#define SL811_USB_STS_SETUP 0x10 ++#define SL811_USB_STS_OVERFLOW 0x20 ++#define SL811_USB_STS_NAK 0x40 ++#define SL811_USB_STS_STALL 0x80 ++ ++/* Control register 1 bits (addr 0x05) */ ++#define SL811_CTRL1_SOF 0x01 ++#define SL811_CTRL1_RESET 0x08 ++#define SL811_CTRL1_JKSTATE 0x10 ++#define SL811_CTRL1_SPEED_LOW 0x20 ++#define SL811_CTRL1_SUSPEND 0x40 ++ ++/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ ++#define SL811_INTR_DONE_A 0x01 ++#define SL811_INTR_DONE_B 0x02 ++#define SL811_INTR_SOF 0x10 ++#define SL811_INTR_INSRMV 0x20 ++#define SL811_INTR_DETECT 0x40 ++#define SL811_INTR_NOTPRESENT 0x40 ++#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ ++ ++/* HW rev and SOF lo register bits (addr 0x0E) */ ++#define SL811_HWR_HWREV 0xF0 ++ ++/* SOF counter and control reg 2 (addr 0x0F) */ ++#define SL811_CTL2_SOFHI 0x3F ++#define SL811_CTL2_DSWAP 0x40 ++#define SL811_CTL2_HOST 0x80 ++ ++/* Set up for 1-ms SOF time. */ ++#define SL811_12M_LOW 0xE0 ++#define SL811_12M_HI 0x2E ++ ++#define SL811_DATA_START 0x10 ++#define SL811_DATA_LIMIT 240 ++ ++ ++/* Requests: bRequest << 8 | bmRequestType */ ++#define RH_GET_STATUS 0x0080 ++#define RH_CLEAR_FEATURE 0x0100 ++#define RH_SET_FEATURE 0x0300 ++#define RH_SET_ADDRESS 0x0500 ++#define RH_GET_DESCRIPTOR 0x0680 ++#define RH_SET_DESCRIPTOR 0x0700 ++#define RH_GET_CONFIGURATION 0x0880 ++#define RH_SET_CONFIGURATION 0x0900 ++#define RH_GET_STATE 0x0280 ++#define RH_GET_INTERFACE 0x0A80 ++#define RH_SET_INTERFACE 0x0B00 ++#define RH_SYNC_FRAME 0x0C80 ++ ++ ++#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) ++ ++/* Virtual Root HUB */ ++struct virt_root_hub { ++ int devnum; /* Address of Root Hub endpoint */ ++ void *urb; /* interrupt URB of root hub */ ++ int send; /* active flag */ ++ int interval; /* intervall of roothub interrupt transfers */ ++ struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ ++}; ++ ++struct sl811_td { ++ /* hardware */ ++ __u8 ctrl; /* control register */ ++ ++ /* write */ ++ __u8 addr; /* base adrress register */ ++ __u8 len; /* base length register */ ++ __u8 pidep; /* PId and endpoint register */ ++ __u8 dev; /* device address register */ ++ ++ /* read */ ++ __u8 status; /* status register */ ++ __u8 left; /* transfer count register */ ++ ++ /* software */ ++ __u8 errcnt; /* error count, begin with 3 */ ++ __u8 done; /* is this td tranfer done */ ++ __u8 *buf; /* point to data buffer for tranfer */ ++ int bustime; /* the bus time need by this td */ ++ int td_status; /* the status of this td */ ++ int nakcnt; /* number of naks */ ++ struct urb *urb; /* the urb this td belongs to */ ++ struct list_head td_list; /* link to a list of the urb */ ++}; ++ ++struct sl811_urb_priv { ++ struct urb *urb; /* the urb this priv beloings to */ ++ struct list_head td_list; /* list of all the td of this urb */ ++ struct sl811_td *cur_td; /* current td is in processing or it will be */ ++ struct sl811_td *first_td; /* the first td of this urb */ ++ struct sl811_td *last_td; /* the last td of this urb */ ++ int interval; /* the query time value for intr urb */ ++ int unlink; /* is the this urb unlinked */ ++ unsigned long inserttime; /* the time when insert to list */ ++}; ++ ++struct sl811_hc { ++ spinlock_t hc_lock; /* Lock for this structure */ ++ ++ int irq; /* IRQ number this hc use */ ++ int addr_io; /* I/O address line address */ ++ int data_io; /* I/O data line address */ ++ struct virt_root_hub rh; /* root hub */ ++ struct usb_port_status rh_status;/* root hub port status */ ++ struct list_head urb_list[6]; /* set of urbs, the order is iso,intr,ctrl,bulk,inactive intr, wait */ ++ struct list_head *cur_list; /* the current list is in process */ ++ wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ ++ struct sl811_td *cur_td; /* point to the td is in process */ ++ struct list_head hc_hcd_list; /* list of all hci_hcd */ ++ struct usb_bus *bus; /* our bus */ ++ int active_urbs; /* total number of active usbs */ ++ int frame_number; /* the current frame number, we do't use it, any one need it? */ ++}; ++ ++#define iso_list urb_list[0] /* set of isoc urbs */ ++#define intr_list urb_list[1] /* ordered (tree) set of int urbs */ ++#define ctrl_list urb_list[2] /* set of ctrl urbs */ ++#define bulk_list urb_list[3] /* set of bulk urbs */ ++#define idle_intr_list urb_list[4] /* set of intr urbs in its idle time*/ ++#define wait_list urb_list[5] /* set of wait urbs */ ++ ++#endif +--- linux-2.4.21/drivers/usb/storage/transport.h~usb-sonycamera ++++ linux-2.4.21/drivers/usb/storage/transport.h +@@ -75,6 +75,8 @@ + #define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ + #endif + ++#define US_PR_DEVICE 0xff /* Use device's value */ ++ + /* + * Bulk only data structures + */ +--- linux-2.4.21/drivers/usb/storage/unusual_devs.h~usb-sonycamera ++++ linux-2.4.21/drivers/usb/storage/unusual_devs.h +@@ -223,10 +223,10 @@ + US_FL_FIX_INQUIRY | US_FL_START_STOP ), + + /* This entry is needed because the device reports Sub=ff */ +-UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0440, ++UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, + "Sony", +- "DSC-S30/S70/S75/505V/F505/F707/F717", +- US_SC_SCSI, US_PR_CB, NULL, ++ "DSC-S30/S70/S75/505V/F505/F707/F717/P8", ++ US_SC_SCSI, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ), + + /* Reported by wim@geeks.nl */ +--- linux-2.4.21/drivers/usb/storage/usb.c~usb-sonycamera ++++ linux-2.4.21/drivers/usb/storage/usb.c +@@ -622,7 +622,9 @@ + + /* Determine subclass and protocol, or copy from the interface */ + subclass = unusual_dev->useProtocol; +- protocol = unusual_dev->useTransport; ++ protocol = (unusual_dev->useTransport == US_PR_DEVICE) ? ++ altsetting->bInterfaceProtocol : ++ unusual_dev->useTransport; + flags = unusual_dev->flags; + + /* +--- linux-2.4.21/drivers/video/fbcon-cfb16.c~fb-turn180 ++++ linux-2.4.21/drivers/video/fbcon-cfb16.c +@@ -34,6 +34,41 @@ + #endif + }; + ++static u8 mirrortab_cfb16[] = { ++ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0, ++ 0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, ++ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8, ++ 0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, ++ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4, ++ 0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, ++ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC, ++ 0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, ++ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2, ++ 0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, ++ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA, ++ 0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, ++ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6, ++ 0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, ++ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE, ++ 0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, ++ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1, ++ 0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, ++ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9, ++ 0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, ++ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5, ++ 0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, ++ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED, ++ 0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, ++ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3, ++ 0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, ++ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB, ++ 0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, ++ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7, ++ 0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, ++ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF, ++ 0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF ++}; ++ + void fbcon_cfb16_setup(struct display *p) + { + p->next_line = p->line_length ? p->line_length : p->var.xres_virtual<<1; +@@ -46,6 +81,53 @@ + int bytes = p->next_line, linesize = bytes * fontheight(p), rows; + u8 *src, *dst; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ char *scrn_end = p->screen_base + p->var.xres*p->var.yres * 2; ++/* ++ printk("---@paul@-------------------------\n"\ ++ "fbcon_cfb16_bmove() %d %d %d %d %d %d\n", ++ sx,sy,dx,dy,height,width ++ ); ++*/ ++ if (sx == 0 && dx == 0 && width * fontwidth(p) * 2 == bytes) ++ { ++ fb_memmove( ++ scrn_end - dy * linesize, ++ scrn_end - sy * linesize, ++ height * linesize ++ ); ++ return; ++ } ++ if (fontwidthlog(p)) { ++ sx <<= fontwidthlog(p)+1; ++ dx <<= fontwidthlog(p)+1; ++ width <<= fontwidthlog(p)+1; ++ } else { ++ sx *= fontwidth(p)*2; ++ dx *= fontwidth(p)*2; ++ width *= fontwidth(p)*2; ++ } ++ if (dy < sy || (dy == sy && dx < sx)) { ++ src = scrn_end + sy * linesize + sx; ++ dst = scrn_end + dy * linesize + dx; ++ for (rows = height * fontheight(p); rows--;) ++ { ++ fb_memmove(dst, src, width); ++ src += bytes; ++ dst += bytes; ++ } ++ } else { ++ src = scrn_end + (sy+height) * linesize + sx - bytes; ++ dst = scrn_end + (dy+height) * linesize + dx - bytes; ++ for (rows = height * fontheight(p); rows--;) ++ { ++ fb_memmove(dst, src, width); ++ src -= bytes; ++ dst -= bytes; ++ } ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { + if (sx == 0 && dx == 0 && width * fontwidth(p) * 2 == bytes) { + fb_memmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, +@@ -78,6 +160,8 @@ + dst -= bytes; + } + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + static inline void rectfill(u8 *dest, int width, int height, u32 data, +@@ -108,10 +192,16 @@ + int bytes = p->next_line, lines = height * fontheight(p); + u32 bgx; + +- dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p) * 2; +- +- bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; +- ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest = p->screen_base ++ + p->var.xres*p->var.yres * 2 ++ - (sy+height) * fontheight(p) * bytes ++ + sx * fontwidth(p) * 2; ++ bgx = 1; ++ } else { ++ dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p) * 2; ++ bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; ++ } + width *= fontwidth(p)/4; + if (width * 8 == bytes) + rectfill(dest, lines * width * 4, 1, bgx, bytes); +@@ -126,14 +216,69 @@ + int bytes = p->next_line, rows; + u32 eorx, fgx, bgx; + +- dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; +- + fgx = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)]; + bgx = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)]; + fgx |= (fgx << 16); + bgx |= (bgx << 16); + eorx = fgx ^ bgx; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest = p->screen_base ++ + p->var.xres*p->var.yres * 2 ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p) * 2; ++ ++ switch (fontwidth(p)) { ++ case 4: ++ cdat = p->fontdata + (c & p->charmask) * fontheight(p); ++ for (rows = fontheight(p); rows--; dest += bytes) ++ { ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); ++ } ++ case 8: ++ cdat = p->fontdata + (c & p->charmask) * fontheight(p); ++ for (rows = fontheight(p); rows--; dest += bytes) ++ { ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ case 12: ++ cdat = p->fontdata + ((c & p->charmask) * fontheight(p) << 1); ++ for (rows = fontheight(p); rows--; dest += bytes) { ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-24); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-20); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-12); ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); ++ } ++ case 16: ++ cdat = p->fontdata + ((c & p->charmask) * fontheight(p) << 1); ++ for (rows = fontheight(p); rows--; dest += bytes) { ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-32); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-28); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-24); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-20); ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { ++ dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; + switch (fontwidth(p)) { + case 4: + case 8: +@@ -167,6 +312,8 @@ + } + break; + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, +@@ -177,7 +324,6 @@ + int rows, bytes = p->next_line; + u32 eorx, fgx, bgx; + +- dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; + c = scr_readw(s); + fgx = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)]; + bgx = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)]; +@@ -185,6 +331,81 @@ + bgx |= (bgx << 16); + eorx = fgx ^ bgx; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest0 = p->screen_base ++ + p->var.xres * p->var.yres * 2 ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p) * 2; ++ ++ switch (fontwidth(p)) { ++ case 4: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + c * fontheight(p); ++ for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) ++ { ++ u8 bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p)*2; ++ } ++ case 8: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + c * fontheight(p); ++ for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) ++ { ++ u8 bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p)*2; ++ } ++ break; ++ case 12: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + (c * fontheight(p) << 1); ++ for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) ++ { ++ u8 bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-24); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-20); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-12); ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p)*2; ++ } ++ case 16: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + (c * fontheight(p) << 1); ++ for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) ++ { ++ u8 bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-32); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-28); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-24); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-20); ++ bits = mirrortab_cfb16[*cdat++]; ++ fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); ++ fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); ++ fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); ++ fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p)*2; ++ } ++ break; ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { ++ dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; + switch (fontwidth(p)) { + case 4: + case 8: +@@ -226,6 +447,8 @@ + } + break; + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb16_revc(struct display *p, int xx, int yy) +@@ -233,6 +456,32 @@ + u8 *dest; + int bytes = p->next_line, rows; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest = p->screen_base ++ + p->var.xres*p->var.yres * 2 ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p) * 2; ++ for (rows = fontheight(p); rows--; dest -= bytes) { ++ switch (fontwidth(p)) { ++ case 16: ++ fb_writel(fb_readl(dest-32) ^ 0xffffffff, dest-32); ++ fb_writel(fb_readl(dest-28) ^ 0xffffffff, dest-28); ++ /* FALL THROUGH */ ++ case 12: ++ fb_writel(fb_readl(dest-24) ^ 0xffffffff, dest-24); ++ fb_writel(fb_readl(dest-20) ^ 0xffffffff, dest-20); ++ /* FALL THROUGH */ ++ case 8: ++ fb_writel(fb_readl(dest-16) ^ 0xffffffff, dest-16); ++ fb_writel(fb_readl(dest-12) ^ 0xffffffff, dest-12); ++ /* FALL THROUGH */ ++ case 4: ++ fb_writel(fb_readl(dest-8) ^ 0xffffffff, dest-8); ++ fb_writel(fb_readl(dest-4) ^ 0xffffffff, dest-4); ++ } ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { + dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2; + for (rows = fontheight(p); rows--; dest += bytes) { + switch (fontwidth(p)) { +@@ -253,6 +502,8 @@ + fb_writel(fb_readl(dest+4) ^ 0xffffffff, dest+4); + } + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb16_clear_margins(struct vc_data *conp, struct display *p, +@@ -268,6 +519,9 @@ + bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; + + if (!bottom_only && (right_width = p->var.xres-right_start)) ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ printk("---@paul@------------------------- fbcon-cfb16 clear margins\n"); ++ } + rectfill(p->screen_base+right_start*2, right_width, + p->var.yres_virtual, bgx, bytes); + if ((bottom_width = p->var.yres-bottom_start)) +--- linux-2.4.21/drivers/video/fbcon-cfb8.c~fb-turn180 ++++ linux-2.4.21/drivers/video/fbcon-cfb8.c +@@ -39,6 +39,41 @@ + #endif + }; + ++static u8 mirrortab_cfb8[] = { ++ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0, ++ 0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, ++ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8, ++ 0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, ++ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4, ++ 0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, ++ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC, ++ 0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, ++ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2, ++ 0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, ++ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA, ++ 0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, ++ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6, ++ 0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, ++ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE, ++ 0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, ++ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1, ++ 0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, ++ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9, ++ 0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, ++ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5, ++ 0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, ++ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED, ++ 0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, ++ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3, ++ 0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, ++ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB, ++ 0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, ++ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7, ++ 0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, ++ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF, ++ 0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF ++}; ++ + void fbcon_cfb8_setup(struct display *p) + { + p->next_line = p->line_length ? p->line_length : p->var.xres_virtual; +@@ -51,10 +86,57 @@ + int bytes = p->next_line, linesize = bytes * fontheight(p), rows; + u8 *src,*dst; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++/* ++ printk("---@paul@-------------------------\n"\ ++ "fbcon_cfb8_bmove() %d %d %d %d %d %d\n", ++ sx,sy,dx,dy,height,width ++ ); ++*/ ++ if (sx == 0 && dx == 0 && width * fontwidth(p) == bytes) ++ { ++ fb_memmove( ++ p->screen_base + p->var.xres*p->var.yres - dy * linesize, ++ p->screen_base + p->var.xres*p->var.yres - sy * linesize, ++ height * linesize); ++ return; ++ } ++ if (fontwidthlog(p)) { ++ sx <<= fontwidthlog(p); dx <<= fontwidthlog(p); width <<= fontwidthlog(p); ++ } else { ++ sx *= fontwidth(p); dx *= fontwidth(p); width *= fontwidth(p); ++ } ++ if (dy < sy || (dy == sy && dx < sx)) ++ { ++ src = p->screen_base + p->var.xres*p->var.yres ++ - sy * linesize - sx; ++ dst = p->screen_base + p->var.xres*p->var.yres ++ - dy * linesize - dx; ++ for (rows = height * fontheight(p) ; rows-- ;) ++ { ++ fb_memmove(dst, src, width); ++ src += bytes; ++ dst += bytes; ++ } ++ } else ++ { ++ src = p->screen_base + p->var.xres*p->var.yres ++ - (sy+height) * linesize - sx + bytes; ++ dst = p->screen_base + p->var.xres*p->var.yres ++ - (dy+height) * linesize - dx + bytes; ++ for (rows = height * fontheight(p) ; rows-- ;) ++ { ++ fb_memmove(dst, src, width); ++ src -= bytes; ++ dst -= bytes; ++ } ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { + if (sx == 0 && dx == 0 && width * fontwidth(p) == bytes) { +- fb_memmove(p->screen_base + dy * linesize, +- p->screen_base + sy * linesize, +- height * linesize); ++ fb_memmove(p->screen_base + dy * linesize, ++ p->screen_base + sy * linesize, ++ height * linesize); + return; + } + if (fontwidthlog(p)) { +@@ -79,6 +161,7 @@ + dst -= bytes; + } + } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + static inline void rectfill(u8 *dest, int width, int height, u8 data, +@@ -97,11 +180,17 @@ + int bytes=p->next_line,lines=height * fontheight(p); + u8 bgx; + +- dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p); +- +- bgx=attr_bgcol_ec(p,conp); +- +- width *= fontwidth(p); ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ bgx=attr_bgcol_ec(p,conp); ++ width *= fontwidth(p); ++ dest = p->screen_base + p->var.xres*p->var.yres ++ - (sy+height) * fontheight(p) * bytes ++ + sx * fontwidth(p); ++ } else { ++ dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p); ++ bgx=attr_bgcol_ec(p,conp); ++ width *= fontwidth(p); ++ } + if (width == bytes) + rectfill(dest, lines * width, 1, bgx, bytes); + else +@@ -114,8 +203,8 @@ + u8 *dest,*cdat; + int bytes=p->next_line,rows; + u32 eorx,fgx,bgx; ++ u8 chrrow; + +- dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); + if (fontwidth(p) <= 8) + cdat = p->fontdata + (c & p->charmask) * fontheight(p); + else +@@ -129,6 +218,53 @@ + bgx |= (bgx << 16); + eorx = fgx ^ bgx; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest = p->screen_base ++ + p->var.xres*p->var.yres ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p); ++ ++ switch (fontwidth(p)) { ++ case 4: ++ for (rows = fontheight(p) ; rows-- ; dest += bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ case 8: ++ for (rows = fontheight(p) ; rows-- ; dest += bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ case 12: ++ for (rows = fontheight(p) ; rows-- ; dest += bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-12); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-8); ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ case 16: ++ for (rows = fontheight(p) ; rows-- ; dest += bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-16); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-12); ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); ++ } ++ break; ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { ++ dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); + switch (fontwidth(p)) { + case 4: + for (rows = fontheight(p) ; rows-- ; dest += bytes) +@@ -152,6 +288,8 @@ + } + break; + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, +@@ -161,8 +299,8 @@ + u16 c; + int rows,bytes=p->next_line; + u32 eorx, fgx, bgx; ++ u8 chrrow; + +- dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); + c = scr_readw(s); + fgx = attr_fgcol(p, c); + bgx = attr_bgcol(p, c); +@@ -171,6 +309,76 @@ + bgx |= (bgx << 8); + bgx |= (bgx << 16); + eorx = fgx ^ bgx; ++ ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest0 = p->screen_base ++ + p->var.xres*p->var.yres ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p); ++ switch (fontwidth(p)) { ++ case 4: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + c * fontheight(p); ++ ++ for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= 4; ++ } ++ break; ++ case 8: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + c * fontheight(p); ++ for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= 8; ++ } ++ break; ++ case 12: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + (c * fontheight(p) << 1); ++ ++ for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-12); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-8); ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p); ++ } ++ break; ++ case 16: ++ while (count--) { ++ c = scr_readw(s++) & p->charmask; ++ cdat = p->fontdata + (c * fontheight(p) << 1); ++ ++ for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) ++ { ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-16); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-12); ++ chrrow = mirrortab_cfb8[*cdat++]; ++ fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); ++ fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); ++ } ++ dest0 -= fontwidth(p); ++ } ++ break; ++ } /* switch (fontwidth(p)) */ ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { ++ dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); + switch (fontwidth(p)) { + case 4: + while (count--) { +@@ -212,6 +420,8 @@ + } + break; + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb8_revc(struct display *p, int xx, int yy) +@@ -219,6 +429,21 @@ + u8 *dest; + int bytes=p->next_line, rows; + ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dest = p->screen_base + p->var.xres*p->var.yres ++ - yy * fontheight(p) * bytes ++ - xx * fontwidth(p); ++ for (rows = fontheight(p) ; rows-- ; dest -= bytes) { ++ switch (fontwidth(p)) { ++ case 16: fb_writel(fb_readl(dest-16) ^ 0x0f0f0f0f, dest-16); /* fall thru */ ++ case 12: fb_writel(fb_readl(dest-12) ^ 0x0f0f0f0f, dest-12); /* fall thru */ ++ case 8: fb_writel(fb_readl(dest-8) ^ 0x0f0f0f0f, dest-8); /* fall thru */ ++ case 4: fb_writel(fb_readl(dest-4) ^ 0x0f0f0f0f, dest-4); /* fall thru */ ++ default: break; ++ } ++ } ++/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ ++ } else { + dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); + for (rows = fontheight(p) ; rows-- ; dest += bytes) { + switch (fontwidth(p)) { +@@ -229,6 +454,8 @@ + default: break; + } + } ++ } ++/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } + + void fbcon_cfb8_clear_margins(struct vc_data *conp, struct display *p, +@@ -244,6 +471,9 @@ + bgx=attr_bgcol_ec(p,conp); + + if (!bottom_only && (right_width = p->var.xres-right_start)) ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ printk("---@paul@------------------------- fbcon-cfb8 clear margins\n"); ++ } + rectfill(p->screen_base+right_start, right_width, p->var.yres_virtual, + bgx, bytes); + if ((bottom_width = p->var.yres-bottom_start)) +--- linux-2.4.21/drivers/video/fbcon.c~fb-turn180 ++++ linux-2.4.21/drivers/video/fbcon.c +@@ -1558,6 +1558,7 @@ + update_region(fg_console, + conp->vc_origin + conp->vc_size_row * conp->vc_top, + conp->vc_size_row * (conp->vc_bottom - conp->vc_top) / 2); ++ conp->vc_top = 0; + return 0; + } + return 1; +@@ -2209,7 +2210,16 @@ + src = logo; + bdepth = depth/8; + for( y1 = 0; y1 < LOGO_H; y1++ ) { +- dst = fb + y1*line + x*bdepth; ++ ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++/* ++ Das ist NICHT die richtige Stelle für den Ramses 16 BPP Modus ++ aber dafür die weiter unten. ++*/ ++ dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; ++ } else { ++ dst = fb + y1*line + x*bdepth; ++ } + for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { + val = (*src << redshift) | + (*src << greenshift) | +@@ -2217,18 +2227,32 @@ + if (bdepth == 4 && !((long)dst & 3)) { + /* Some cards require 32bit access */ + fb_writel (val, dst); +- dst += 4; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst -= 4; ++ } else { ++ dst += 4; ++ } + } else if (bdepth == 2 && !((long)dst & 1)) { + /* others require 16bit access */ + fb_writew (val,dst); +- dst +=2; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst -= 2; ++ } else { ++ dst +=2; ++ } + } else { ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ for( i = bdepth-1; i >= 0; --i ) ++ fb_writeb (val >> (i*8), dst--); ++ ++ } else { + #ifdef __LITTLE_ENDIAN +- for( i = 0; i < bdepth; ++i ) ++ for( i = 0; i < bdepth; ++i ) + #else +- for( i = bdepth-1; i >= 0; --i ) ++ for( i = bdepth-1; i >= 0; --i ) + #endif +- fb_writeb (val >> (i*8), dst++); ++ fb_writeb (val >> (i*8), dst++); ++ } + } + } + } +@@ -2239,28 +2263,42 @@ + src = linux_logo16; + bdepth = (depth+7)/8; + for( y1 = 0; y1 < LOGO_H; y1++ ) { +- dst = fb + y1*line + x*bdepth; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; ++ } else { ++ dst = fb + y1*line + x*bdepth; ++ } + for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) { + pix = *src >> 4; /* upper nibble */ + val = (pix << redshift) | + (pix << greenshift) | + (pix << blueshift); ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ for( i = bdepth-1; i >= 0; --i ) ++ fb_writeb (val >> (i*8), dst--); ++ } else { + #ifdef __LITTLE_ENDIAN +- for( i = 0; i < bdepth; ++i ) ++ for( i = 0; i < bdepth; ++i ) + #else +- for( i = bdepth-1; i >= 0; --i ) ++ for( i = bdepth-1; i >= 0; --i ) + #endif +- fb_writeb (val >> (i*8), dst++); ++ fb_writeb (val >> (i*8), dst++); ++ } + pix = *src & 0x0f; /* lower nibble */ + val = (pix << redshift) | + (pix << greenshift) | + (pix << blueshift); ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ for( i = bdepth-1; i >= 0; --i ) ++ fb_writeb (val >> (i*8), dst--); ++ } else { + #ifdef __LITTLE_ENDIAN +- for( i = 0; i < bdepth; ++i ) ++ for( i = 0; i < bdepth; ++i ) + #else +- for( i = bdepth-1; i >= 0; --i ) ++ for( i = bdepth-1; i >= 0; --i ) + #endif +- fb_writeb (val >> (i*8), dst++); ++ fb_writeb (val >> (i*8), dst++); ++ } + } + } + } +@@ -2287,7 +2325,11 @@ + + src = logo; + for( y1 = 0; y1 < LOGO_H; y1++ ) { +- dst = fb + y1*line + x*bdepth; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; ++ } else { ++ dst = fb + y1*line + x*bdepth; ++ } + for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { + val = safe_shift((linux_logo_red[*src-32] & redmask), redshift) | + safe_shift((linux_logo_green[*src-32] & greenmask), greenshift) | +@@ -2295,18 +2337,31 @@ + if (bdepth == 4 && !((long)dst & 3)) { + /* Some cards require 32bit access */ + fb_writel (val, dst); +- dst += 4; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst -= 4; ++ } else { ++ dst += 4; ++ } + } else if (bdepth == 2 && !((long)dst & 1)) { + /* others require 16bit access */ + fb_writew (val,dst); +- dst +=2; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ dst -= 2; ++ } else { ++ dst +=2; ++ } + } else { ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ for( i = bdepth-1; i >= 0; --i ) ++ fb_writeb (val >> (i*8), dst--); ++ } else { + #ifdef __LITTLE_ENDIAN +- for( i = 0; i < bdepth; ++i ) ++ for( i = 0; i < bdepth; ++i ) + #else +- for( i = bdepth-1; i >= 0; --i ) ++ for( i = bdepth-1; i >= 0; --i ) + #endif +- fb_writeb (val >> (i*8), dst++); ++ fb_writeb (val >> (i*8), dst++); ++ } + } + } + } +@@ -2331,13 +2386,24 @@ + if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) { + /* depth 8 or more, packed, with color registers */ + +- src = logo; +- for( y1 = 0; y1 < LOGO_H; y1++ ) { +- dst = fb + y1*line + x; +- for( x1 = 0; x1 < LOGO_W; x1++ ) +- fb_writeb (*src++, dst++); +- } +- done = 1; ++ if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { ++ src = logo; ++ for( y1 = 0; y1 < LOGO_H; y1++ ) ++ { ++ dst = fb + p->var.xres*p->var.yres -1 - y1*line - x; ++ for( x1 = 0; x1 < LOGO_W; x1++ ) ++ fb_writeb (*src++, dst--); ++ } ++ done = 1; ++ } else { ++ src = logo; ++ for( y1 = 0; y1 < LOGO_H; y1++ ) { ++ dst = fb + y1*line + x; ++ for( x1 = 0; x1 < LOGO_W; x1++ ) ++ fb_writeb (*src++, dst++); ++ } ++ done = 1; ++ } + } + #endif + #if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \ +--- linux-2.4.21/drivers/video/fbmem.c~fb-buffered ++++ linux-2.4.21/drivers/video/fbmem.c +@@ -302,7 +302,7 @@ + { "sa1100", sa1100fb_init, NULL }, + #endif + #ifdef CONFIG_FB_PXA +- { "pxa", pxafb_init, NULL }, ++ { "pxa", pxafb_init, NULL }, + #endif + #ifdef CONFIG_FB_SUN3 + { "sun3", sun3fb_init, sun3fb_setup }, +@@ -672,7 +672,11 @@ + #elif defined(__hppa__) + pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; + #elif defined(__ia64__) || defined(__arm__) ++#ifdef CONFIG_PXA ++ vma->vm_page_prot = pgprot_noncached_buffered(vma->vm_page_prot); ++#else + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++#endif + #elif defined(__hppa__) + pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; + #else +--- linux-2.4.21/drivers/video/pxafb.c~ramses-lcd ++++ linux-2.4.21/drivers/video/pxafb.c +@@ -45,8 +45,6 @@ + + #include