some MNCI "Ramses" fixes/updates
authorHolger Schurig <schurig@mn-solutions.de>
Wed, 9 Mar 2005 15:05:47 +0000 (15:05 +0000)
committerHolger Schurig <schurig@mn-solutions.de>
Wed, 9 Mar 2005 15:05:47 +0000 (15:05 +0000)
BKrev: 422f10cb2lTTtOfeHYxEiXthG3dCnQ

conf/distro/openmnci.conf
packages/busybox/busybox-1.00/ramses/defconfig
packages/busybox/files/ramses/defconfig [deleted file]
packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb

index fb46dc2..6442309 100644 (file)
@@ -3,7 +3,7 @@ INHERIT += "debian"
 
 
 #CVSDATE = 20050107
-CVSDATE = 20050216
+#CVSDATE = 20050216
 CVSDATE_ipkg-utils = 20050110
 
 #ASSUME_PROVIDED = "virtual/arm-linux-gcc-2.95"
index 94f175b..f98af91 100644 (file)
@@ -111,7 +111,7 @@ CONFIG_FEATURE_LS_RECURSIVE=y
 CONFIG_FEATURE_LS_SORTFILES=y
 CONFIG_FEATURE_LS_TIMESTAMPS=y
 CONFIG_FEATURE_LS_USERNAME=y
-CONFIG_FEATURE_LS_COLOR=y
+# CONFIG_FEATURE_LS_COLOR is not set
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
 CONFIG_MKFIFO=y
@@ -284,7 +284,7 @@ CONFIG_DC=y
 # CONFIG_MT is not set
 # CONFIG_RX is not set
 CONFIG_STRINGS=y
-# CONFIG_TIME is not set
+CONFIG_TIME=y
 # CONFIG_WATCHDOG is not set
 
 #
@@ -301,7 +301,7 @@ CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL=y
 CONFIG_LSMOD=y
 CONFIG_MODPROBE=y
 CONFIG_RMMOD=y
-CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
 
 #
 # Networking Utilities
diff --git a/packages/busybox/files/ramses/defconfig b/packages/busybox/files/ramses/defconfig
deleted file mode 100644 (file)
index e69de29..0000000
index e69de29..fd1ba4d 100644 (file)
+
+# 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 @@
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
++
++<book id="Reed-Solomon-Library-Guide">
++ <bookinfo>
++  <title>Reed-Solomon Library Programming Interface</title>
++  
++  <authorgroup>
++   <author>
++    <firstname>Thomas</firstname>
++    <surname>Gleixner</surname>
++    <affiliation>
++     <address>
++      <email>tglx@linutronix.de</email>
++     </address>
++    </affiliation>
++   </author>
++  </authorgroup>
++
++  <copyright>
++   <year>2004</year>
++   <holder>Thomas Gleixner</holder>
++  </copyright>
++
++  <legalnotice>
++   <para>
++     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.
++   </para>
++      
++   <para>
++     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.
++   </para>
++      
++   <para>
++     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
++   </para>
++      
++   <para>
++     For more details see the file COPYING in the source
++     distribution of Linux.
++   </para>
++  </legalnotice>
++ </bookinfo>
++
++<toc></toc>
++
++  <chapter id="intro">
++      <title>Introduction</title>
++  <para>
++      The generic Reed-Solomon Library provides encoding, decoding
++      and error correction functions.
++  </para>
++  <para>
++      Reed-Solomon codes are used in communication and storage
++      applications to ensure data integrity. 
++  </para>
++  <para>
++      This documentation is provided for developers who want to utilize
++      the functions provided by the library.
++  </para>
++  </chapter>
++  
++  <chapter id="bugs">
++     <title>Known Bugs And Assumptions</title>
++  <para>
++      None.   
++  </para>
++  </chapter>
++
++  <chapter id="usage">
++      <title>Usage</title>
++      <para>
++              This chapter provides examples how to use the library.
++      </para>
++      <sect1>
++              <title>Initializing</title>
++              <para>
++                      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.
++              </para>
++              <programlisting>
++/* 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);
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Encoding</title>
++              <para>
++                      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.
++              </para>
++              <para>
++                      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.
++              </para>
++              <para>
++                      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.
++              </para>
++              <programlisting>
++/* 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);
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Decoding</title>
++              <para>
++                      The decoder calculates the syndrome over
++                      the given data length and the received parity symbols
++                      and corrects errors in the data.
++              </para>
++              <para>
++                      If a syndrome is available from a hardware decoder
++                      then the syndrome calculation is skipped.
++              </para>
++              <para>
++                      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.
++              </para>
++              <para>
++                      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.
++              </para>
++              
++              <sect2>
++              <title>
++                      Decoding with syndrome calculation, direct data correction
++              </title>
++              <programlisting>
++/* 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);
++              </programlisting>
++              </sect2>
++
++              <sect2>
++              <title>
++                      Decoding with syndrome given by hardware decoder, direct data correction
++              </title>
++              <programlisting>
++/* 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);
++              </programlisting>
++              </sect2>
++
++              <sect2>
++              <title>
++                      Decoding with syndrome given by hardware decoder, no direct data correction.
++              </title>
++              <para>
++                      Note: It's not necessary to give data and received parity to the decoder.
++              </para>
++              <programlisting>
++/* 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]);
++}
++              </programlisting>
++              </sect2>
++      </sect1>
++      <sect1>
++              <title>Cleanup</title>
++              <para>
++                      The function free_rs frees the allocated resources,
++                      if the caller is the last user of the decoder.
++              </para>
++              <programlisting>
++/* Release resources */
++free_rs(rs_decoder);
++              </programlisting>
++      </sect1>
++
++  </chapter>
++      
++  <chapter id="structs">
++     <title>Structures</title>
++     <para>
++     This chapter contains the autogenerated documentation of the structures which are
++     used in the Reed-Solomon Library and are relevant for a developer.
++     </para>
++!Iinclude/linux/rslib.h
++  </chapter>
++
++  <chapter id="pubfunctions">
++     <title>Public Functions Provided</title>
++     <para>
++     This chapter contains the autogenerated documentation of the Reed-Solomon functions
++     which are exported.
++     </para>
++!Elib/reed_solomon/reed_solomon.c
++  </chapter>
++  
++  <chapter id="credits">
++     <title>Credits</title>
++      <para>
++              The library code for encoding and decoding was written by Phil Karn.
++      </para>
++      <programlisting>
++              Copyright 2002, Phil Karn, KA9Q
++              May be used under the terms of the GNU General Public License (GPL)
++      </programlisting>
++      <para>
++              The wrapper functions and interfaces are written by Thomas Gleixner
++      </para>
++      <para>
++              Many users have provided bugfixes, improvements and helping hands for testing.
++              Thanks a lot.
++      </para>
++      <para>
++              The following people have contributed to this document:
++      </para>
++      <para>
++              Thomas Gleixner<email>tglx@linutronix.de</email>
++      </para>
++  </chapter>
++</book>
+--- /dev/null
++++ linux-2.4.21/Documentation/DocBook/mtdnand.tmpl
+@@ -0,0 +1,1318 @@
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
++
++<book id="MTD-NAND-Guide">
++ <bookinfo>
++  <title>MTD NAND Driver Programming Interface</title>
++  
++  <authorgroup>
++   <author>
++    <firstname>Thomas</firstname>
++    <surname>Gleixner</surname>
++    <affiliation>
++     <address>
++      <email>tglx@linutronix.de</email>
++     </address>
++    </affiliation>
++   </author>
++  </authorgroup>
++
++  <copyright>
++   <year>2004</year>
++   <holder>Thomas Gleixner</holder>
++  </copyright>
++
++  <legalnotice>
++   <para>
++     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.
++   </para>
++      
++   <para>
++     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.
++   </para>
++      
++   <para>
++     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
++   </para>
++      
++   <para>
++     For more details see the file COPYING in the source
++     distribution of Linux.
++   </para>
++  </legalnotice>
++ </bookinfo>
++
++<toc></toc>
++
++  <chapter id="intro">
++      <title>Introduction</title>
++  <para>
++      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.
++  </para>
++  <para>
++      This documentation is provided for developers who want to implement
++      board drivers or filesystem drivers suitable for NAND devices.
++  </para>
++  </chapter>
++  
++  <chapter id="bugs">
++     <title>Known Bugs And Assumptions</title>
++  <para>
++      None.   
++  </para>
++  </chapter>
++
++  <chapter id="dochints">
++     <title>Documentation hints</title>
++     <para>
++     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.
++     </para>
++     <sect1>   
++      <title>Function identifiers [XXX]</title>
++      <para>
++      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:
++      </para>
++      <itemizedlist>
++              <listitem><para>
++              [MTD Interface]</para><para>
++              These functions provide the interface to the MTD kernel API. 
++              They are not replacable and provide functionality
++              which is complete hardware independent.
++              </para></listitem>
++              <listitem><para>
++              [NAND Interface]</para><para>
++              These functions are exported and provide the interface to the NAND kernel API. 
++              </para></listitem>
++              <listitem><para>
++              [GENERIC]</para><para>
++              Generic functions are not replacable and provide functionality
++              which is complete hardware independent.
++              </para></listitem>
++              <listitem><para>
++              [DEFAULT]</para><para>
++              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.
++              </para></listitem>
++      </itemizedlist>
++     </sect1>
++     <sect1>   
++      <title>Struct member identifiers [XXX]</title>
++      <para>
++      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:
++      </para>
++      <itemizedlist>
++              <listitem><para>
++              [INTERN]</para><para>
++              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().
++              </para></listitem>
++              <listitem><para>
++              [REPLACEABLE]</para><para>
++              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.
++              </para></listitem>
++              <listitem><para>
++              [BOARDSPECIFIC]</para><para>
++              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().
++              </para></listitem>
++              <listitem><para>
++              [OPTIONAL]</para><para>
++              Optional members can hold information relevant for the board driver. The
++              generic NAND driver code does not use this information.
++              </para></listitem>
++      </itemizedlist>
++     </sect1>
++  </chapter>   
++
++  <chapter id="basicboarddriver">
++      <title>Basic board driver</title>
++      <para>
++              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.
++      </para>
++      <sect1>
++              <title>Basic defines</title>
++              <para>
++                      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.
++              </para>
++              <para>
++                      Kmalloc based example
++              </para>
++              <programlisting>
++static struct mtd_info *board_mtd;
++static unsigned long baseaddr;
++              </programlisting>
++              <para>
++                      Static example
++              </para>
++              <programlisting>
++static struct mtd_info board_mtd;
++static struct nand_chip board_chip;
++static unsigned long baseaddr;
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Partition defines</title>
++              <para>
++                      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.
++              </para>
++              <programlisting>
++#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 },
++};
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Hardware control function</title>
++              <para>
++                      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.
++              </para>
++              <para>
++                      <emphasis>GPIO based example</emphasis>
++              </para>
++              <programlisting>
++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;
++      }
++}
++              </programlisting>
++              <para>
++                      <emphasis>Address lines based example.</emphasis> It's assumed that the
++                      nCE pin is driven by a chip select decoder.
++              </para>
++              <programlisting>
++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;
++      }
++}
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Device ready function</title>
++              <para>
++                      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.               
++              </para>
++      </sect1>
++      <sect1>
++              <title>Init function</title>
++              <para>
++                      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.
++              </para>
++              <programlisting>
++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);
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Exit function</title>
++              <para>
++                      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.
++              </para>
++              <programlisting>
++#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
++              </programlisting>
++      </sect1>
++  </chapter>
++
++  <chapter id="boarddriversadvanced">
++      <title>Advanced board driver functions</title>
++      <para>
++              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.
++      </para>
++      <sect1>
++              <title>Multiple chip control</title>
++              <para>
++                      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.
++              </para>
++              <para>
++                      The nand driver concatenates the chips to one virtual
++                      chip and provides this virtual chip to the MTD layer.
++              </para>
++              <para>
++                      <emphasis>Note: The driver can only handle linear chip arrays
++                      of equally sized chips. There is no support for
++                      parallel arrays which extend the buswidth.</emphasis>
++              </para>
++              <para>
++                      <emphasis>GPIO based example</emphasis>
++              </para>
++              <programlisting>
++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);  
++}
++              </programlisting>
++              <para>
++                      <emphasis>Address lines based example.</emphasis>
++                      Its assumed that the nCE pins are connected to an
++                      address decoder.
++              </para>
++              <programlisting>
++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;
++      }       
++}
++              </programlisting>
++      </sect1>
++      <sect1>
++              <title>Hardware ECC support</title>
++              <sect2>
++                      <title>Functions and constants</title>
++                      <para>
++                              The nand driver supports three different types of
++                              hardware ECC.
++                              <itemizedlist>
++                              <listitem><para>NAND_ECC_HW3_256</para><para>
++                              Hardware ECC generator providing 3 bytes ECC per
++                              256 byte.
++                              </para> </listitem>
++                              <listitem><para>NAND_ECC_HW3_512</para><para>
++                              Hardware ECC generator providing 3 bytes ECC per
++                              512 byte.
++                              </para> </listitem>
++                              <listitem><para>NAND_ECC_HW6_512</para><para>
++                              Hardware ECC generator providing 6 bytes ECC per
++                              512 byte.
++                              </para> </listitem>
++                              <listitem><para>NAND_ECC_HW8_512</para><para>
++                              Hardware ECC generator providing 6 bytes ECC per
++                              512 byte.
++                              </para> </listitem>
++                              </itemizedlist>
++                              If your hardware generator has a different functionality
++                              add it at the appropriate place in nand_base.c
++                      </para>
++                      <para>
++                              The board driver must provide following functions:
++                              <itemizedlist>
++                              <listitem><para>enable_hwecc</para><para>
++                              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.
++                              </para> </listitem>
++                              <listitem><para>calculate_ecc</para><para>
++                              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.
++                              </para> </listitem>
++                              <listitem><para>correct_data</para><para>
++                              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.
++                              </para> </listitem>
++                              </itemizedlist>
++                      </para>
++              </sect2>
++              <sect2>
++              <title>Hardware ECC with syndrome calculation</title>
++                      <para>
++                              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.
++                      </para>
++                      <para>
++                              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.
++                      </para>
++              </sect2>
++      </sect1>
++      <sect1>
++              <title>Bad block table support</title>
++              <para>
++                      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.
++              </para>
++              <para>
++                      The nand driver supports various types of bad block
++                      tables.
++                      <itemizedlist>
++                      <listitem><para>Per device</para><para>
++                      The bad block table contains all bad block information
++                      of the device which can consist of multiple chips.
++                      </para> </listitem>
++                      <listitem><para>Per chip</para><para>
++                      A bad block table is used per chip and contains the
++                      bad block information for this particular chip.
++                      </para> </listitem>
++                      <listitem><para>Fixed offset</para><para>
++                      The bad block table is located at a fixed offset
++                      in the chip (device). This applies to various
++                      DiskOnChip devices.
++                      </para> </listitem>
++                      <listitem><para>Automatic placed</para><para>
++                      The bad block table is automatically placed and
++                      detected either at the end or at the beginning
++                      of a chip (device)
++                      </para> </listitem>
++                      <listitem><para>Mirrored tables</para><para>
++                      The bad block table is mirrored on the chip (device) to
++                      allow updates of the bad block table without data loss.
++                      </para> </listitem>
++                      </itemizedlist>
++              </para>
++              <para>  
++                      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().
++              </para>
++              <para>
++                      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.
++              </para>
++              <sect2>
++                      <title>Flash based tables</title>
++                      <para>
++                              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.
++                      </para>
++                      <para>
++                              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.
++                      </para>
++                      <para>
++                              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
++                              <itemizedlist>
++                              <listitem><para>Store bad block table per chip</para></listitem>
++                              <listitem><para>Use 2 bits per block</para></listitem>
++                              <listitem><para>Automatic placement at the end of the chip</para></listitem>
++                              <listitem><para>Use mirrored tables with version numbers</para></listitem>
++                              <listitem><para>Reserve 4 blocks at the end of the chip</para></listitem>
++                              </itemizedlist>
++                      </para>
++              </sect2>
++              <sect2>
++                      <title>User defined tables</title>
++                      <para>
++                              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.
++                      </para>
++                      <para>
++                              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.
++                              <itemizedlist>
++                              <listitem><para>Number of bits per block</para>
++                              <para>The supported number of bits is 1, 2, 4, 8.</para></listitem>
++                              <listitem><para>Table per chip</para>
++                              <para>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.</para></listitem>
++                              <listitem><para>Table location is absolute</para>
++                              <para>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</para></listitem>
++                              <listitem><para>Table location is automatically detected</para>
++                              <para>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.</para></listitem>
++                              <listitem><para>Table creation</para>
++                              <para>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. </para></listitem>
++                              <listitem><para>Table write support</para>
++                              <para>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.</para>
++                              <para>
++                              Note: Write support should only be enabled for mirrored tables with
++                              version control.
++                              </para></listitem>
++                              <listitem><para>Table version control</para>
++                              <para>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.</para></listitem>
++                              <listitem><para>Save block contents on write</para>
++                              <para>
++                              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.
++                              </para></listitem>
++                              <listitem><para>Number of reserved blocks</para>
++                              <para>
++                              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.
++                              </para></listitem>
++                              </itemizedlist>
++                      </para>
++              </sect2>
++      </sect1>
++      <sect1>
++              <title>Spare area (auto)placement</title>
++              <para>
++                      The nand driver implements different possibilities for
++                      placement of filesystem data in the spare area, 
++                      <itemizedlist>
++                      <listitem><para>Placement defined by fs driver</para></listitem>
++                      <listitem><para>Automatic placement</para></listitem>
++                      </itemizedlist>
++                      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.
++              </para>
++              <para>
++                      File system drivers can provide a own placement scheme which
++                      is used instead of the default placement scheme.
++              </para>
++              <para>
++                      Placement schemes are defined by a nand_oobinfo structure
++                      <programlisting>
++struct nand_oobinfo {
++      int     useecc;
++      int     eccbytes;
++      int     eccpos[24];
++      int     oobfree[8][2];
++};
++                      </programlisting>
++                      <itemizedlist>
++                      <listitem><para>useecc</para><para>
++                              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.
++                      </para></listitem>
++                      <listitem><para>eccbytes</para><para>
++                              The eccbytes member defines the number of ecc bytes per page.
++                      </para></listitem>
++                      <listitem><para>eccpos</para><para>
++                              The eccpos array holds the byte offsets in the spare area where
++                              the ecc codes are placed.
++                      </para></listitem>
++                      <listitem><para>oobfree</para><para>
++                              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.
++                      </para></listitem>
++                      </itemizedlist>
++              </para>
++              <sect2>
++                      <title>Placement defined by fs driver</title>
++                      <para>
++                              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 
++                      </para>
++                      <para>
++                              &lt;spare data page 0&gt;&lt;ecc result 0&gt;...&lt;ecc result n&gt;
++                      </para>
++                      <para>
++                              ...
++                      </para>
++                      <para>
++                              &lt;spare data page n&gt;&lt;ecc result 0&gt;...&lt;ecc result n&gt;
++                      </para>
++                      <para>
++                              This is a legacy mode used by YAFFS1.
++                      </para>
++                      <para>
++                              If the spare area buffer is NULL then only the ECC placement is
++                              done according to the given scheme in the nand_oobinfo structure.
++                      </para>
++              </sect2>
++              <sect2>
++                      <title>Automatic placement</title>
++                      <para>
++                              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.
++                      </para>
++                      <para>
++                              If the spare area buffer is NULL then only the ECC placement is
++                              done according to the default builtin scheme.
++                      </para>
++              </sect2>
++              <sect2>
++                      <title>User space placement selection</title>
++              <para>
++                      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.
++                      <programlisting>
++      ioctl (fd, MEMSETOOBSEL, oobsel);
++                      </programlisting>
++                      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.
++              </para>
++              </sect2>
++      </sect1>        
++      <sect1>
++              <title>Spare area autoplacement default schemes</title>
++              <sect2>
++                      <title>256 byte pagesize</title>
++<informaltable><tgroup cols="3"><tbody>
++<row>
++<entry>Offset</entry>
++<entry>Content</entry>
++<entry>Comment</entry>
++</row>
++<row>
++<entry>0x00</entry>
++<entry>ECC byte 0</entry>
++<entry>Error correction code byte 0</entry>
++</row>
++<row>
++<entry>0x01</entry>
++<entry>ECC byte 1</entry>
++<entry>Error correction code byte 1</entry>
++</row>
++<row>
++<entry>0x02</entry>
++<entry>ECC byte 2</entry>
++<entry>Error correction code byte 2</entry>
++</row>
++<row>
++<entry>0x03</entry>
++<entry>Autoplace 0</entry>
++<entry></entry>
++</row>
++<row>
++<entry>0x04</entry>
++<entry>Autoplace 1</entry>
++<entry></entry>
++</row>
++<row>
++<entry>0x05</entry>
++<entry>Bad block marker</entry>
++<entry>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</entry>
++</row>
++<row>
++<entry>0x06</entry>
++<entry>Autoplace 2</entry>
++<entry></entry>
++</row>
++<row>
++<entry>0x07</entry>
++<entry>Autoplace 3</entry>
++<entry></entry>
++</row>
++</tbody></tgroup></informaltable>
++              </sect2>
++              <sect2>
++                      <title>512 byte pagesize</title>
++<informaltable><tgroup cols="3"><tbody>
++<row>
++<entry>Offset</entry>
++<entry>Content</entry>
++<entry>Comment</entry>
++</row>
++<row>
++<entry>0x00</entry>
++<entry>ECC byte 0</entry>
++<entry>Error correction code byte 0 of the lower 256 Byte data in
++this page</entry>
++</row>
++<row>
++<entry>0x01</entry>
++<entry>ECC byte 1</entry>
++<entry>Error correction code byte 1 of the lower 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x02</entry>
++<entry>ECC byte 2</entry>
++<entry>Error correction code byte 2 of the lower 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x03</entry>
++<entry>ECC byte 3</entry>
++<entry>Error correction code byte 0 of the upper 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x04</entry>
++<entry>reserved</entry>
++<entry>reserved</entry>
++</row>
++<row>
++<entry>0x05</entry>
++<entry>Bad block marker</entry>
++<entry>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</entry>
++</row>
++<row>
++<entry>0x06</entry>
++<entry>ECC byte 4</entry>
++<entry>Error correction code byte 1 of the upper 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x07</entry>
++<entry>ECC byte 5</entry>
++<entry>Error correction code byte 2 of the upper 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x08 - 0x0F</entry>
++<entry>Autoplace 0 - 7</entry>
++<entry></entry>
++</row>
++</tbody></tgroup></informaltable>
++              </sect2>
++              <sect2>
++                      <title>2048 byte pagesize</title>
++<informaltable><tgroup cols="3"><tbody>
++<row>
++<entry>Offset</entry>
++<entry>Content</entry>
++<entry>Comment</entry>
++</row>
++<row>
++<entry>0x00</entry>
++<entry>Bad block marker</entry>
++<entry>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</entry>
++</row>
++<row>
++<entry>0x01</entry>
++<entry>Reserved</entry>
++<entry>Reserved</entry>
++</row>
++<row>
++<entry>0x02-0x27</entry>
++<entry>Autoplace 0 - 37</entry>
++<entry></entry>
++</row>
++<row>
++<entry>0x28</entry>
++<entry>ECC byte 0</entry>
++<entry>Error correction code byte 0 of the first 256 Byte data in
++this page</entry>
++</row>
++<row>
++<entry>0x29</entry>
++<entry>ECC byte 1</entry>
++<entry>Error correction code byte 1 of the first 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x2A</entry>
++<entry>ECC byte 2</entry>
++<entry>Error correction code byte 2 of the first 256 Bytes data in
++this page</entry>
++</row>
++<row>
++<entry>0x2B</entry>
++<entry>ECC byte 3</entry>
++<entry>Error correction code byte 0 of the second 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x2C</entry>
++<entry>ECC byte 4</entry>
++<entry>Error correction code byte 1 of the second 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x2D</entry>
++<entry>ECC byte 5</entry>
++<entry>Error correction code byte 2 of the second 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x2E</entry>
++<entry>ECC byte 6</entry>
++<entry>Error correction code byte 0 of the third 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x2F</entry>
++<entry>ECC byte 7</entry>
++<entry>Error correction code byte 1 of the third 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x30</entry>
++<entry>ECC byte 8</entry>
++<entry>Error correction code byte 2 of the third 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x31</entry>
++<entry>ECC byte 9</entry>
++<entry>Error correction code byte 0 of the fourth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x32</entry>
++<entry>ECC byte 10</entry>
++<entry>Error correction code byte 1 of the fourth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x33</entry>
++<entry>ECC byte 11</entry>
++<entry>Error correction code byte 2 of the fourth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x34</entry>
++<entry>ECC byte 12</entry>
++<entry>Error correction code byte 0 of the fifth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x35</entry>
++<entry>ECC byte 13</entry>
++<entry>Error correction code byte 1 of the fifth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x36</entry>
++<entry>ECC byte 14</entry>
++<entry>Error correction code byte 2 of the fifth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x37</entry>
++<entry>ECC byte 15</entry>
++<entry>Error correction code byte 0 of the sixt 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x38</entry>
++<entry>ECC byte 16</entry>
++<entry>Error correction code byte 1 of the sixt 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x39</entry>
++<entry>ECC byte 17</entry>
++<entry>Error correction code byte 2 of the sixt 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x3A</entry>
++<entry>ECC byte 18</entry>
++<entry>Error correction code byte 0 of the seventh 256 Bytes of
++data in this page</entry>
++</row>
++<row>
++<entry>0x3B</entry>
++<entry>ECC byte 19</entry>
++<entry>Error correction code byte 1 of the seventh 256 Bytes of
++data in this page</entry>
++</row>
++<row>
++<entry>0x3C</entry>
++<entry>ECC byte 20</entry>
++<entry>Error correction code byte 2 of the seventh 256 Bytes of
++data in this page</entry>
++</row>
++<row>
++<entry>0x3D</entry>
++<entry>ECC byte 21</entry>
++<entry>Error correction code byte 0 of the eigth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x3E</entry>
++<entry>ECC byte 22</entry>
++<entry>Error correction code byte 1 of the eigth 256 Bytes of data
++in this page</entry>
++</row>
++<row>
++<entry>0x3F</entry>
++<entry>ECC byte 23</entry>
++<entry>Error correction code byte 2 of the eigth 256 Bytes of data
++in this page</entry>
++</row>
++</tbody></tgroup></informaltable>
++              </sect2>
++      </sect1>
++  </chapter>
++
++  <chapter id="filesystems">
++      <title>Filesystem support</title>
++      <para>
++              The NAND driver provides all neccecary functions for a
++              filesystem via the MTD interface.
++      </para>
++      <para>
++              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. 
++      </para>
++      <para>
++              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.               
++      </para>
++      <para>
++              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.
++      </para>
++  </chapter>  
++  <chapter id="tools">
++      <title>Tools</title>
++      <para>
++              The MTD project provides a couple of helpful tools to handle NAND Flash.
++              <itemizedlist>
++              <listitem><para>flasherase, flasheraseall: Erase and format FLASH partitions</para></listitem>
++              <listitem><para>nandwrite: write filesystem images to NAND FLASH</para></listitem>
++              <listitem><para>nanddump: dump the contents of a NAND FLASH partitions</para></listitem>
++              </itemizedlist>
++      </para>
++      <para>
++              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.
++      </para>
++  </chapter>  
++
++  <chapter id="defines">
++     <title>Constants</title>
++     <para>
++     This chapter describes the constants which might be relevant for a driver developer.
++     </para>
++     <sect1>   
++      <title>Chip option constants</title>
++      <sect2>   
++              <title>Constants for chip id table</title>
++              <para>
++              These constants are defined in nand.h. They are ored together to describe
++              the chip functionality.
++              <programlisting>
++/* 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 
++              </programlisting>
++              </para>
++      </sect2>
++      <sect2>   
++              <title>Constants for runtime options</title>
++              <para>
++              These constants are defined in nand.h. They are ored together to describe
++              the functionality.
++              <programlisting>
++/* 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
++              </programlisting>
++              </para>
++      </sect2>
++     </sect1> 
++
++     <sect1>   
++      <title>ECC selection constants</title>
++      <para>
++      Use these constants to select the ECC algorithm.
++      <programlisting>
++/* 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
++      </programlisting>
++      </para>
++     </sect1> 
++
++     <sect1>   
++      <title>Hardware control related constants</title>
++      <para>
++      These constants describe the requested hardware access function when
++      the boardspecific hardware control function is called
++      <programlisting>
++/* 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
++      </programlisting>
++      </para>
++     </sect1> 
++
++     <sect1>   
++      <title>Bad block table related constants</title>
++      <para>
++      These constants describe the options used for bad block
++      table descriptors.
++      <programlisting>
++/* 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
++      </programlisting>
++      </para>
++     </sect1> 
++
++  </chapter>
++      
++  <chapter id="structs">
++     <title>Structures</title>
++     <para>
++     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.
++     </para>
++!Iinclude/linux/mtd/nand.h
++  </chapter>
++
++  <chapter id="pubfunctions">
++     <title>Public Functions Provided</title>
++     <para>
++     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.
++     </para>
++!Edrivers/mtd/nand/nand_base.c
++!Edrivers/mtd/nand/nand_bbt.c
++!Edrivers/mtd/nand/nand_ecc.c
++  </chapter>
++  
++  <chapter id="intfunctions">
++     <title>Internal Functions Provided</title>
++     <para>
++     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.
++     </para>
++!Idrivers/mtd/nand/nand_base.c
++!Idrivers/mtd/nand/nand_bbt.c
++!Idrivers/mtd/nand/nand_ecc.c
++  </chapter>
++
++  <chapter id="credits">
++     <title>Credits</title>
++      <para>
++              The following people have contributed to the NAND driver:
++              <orderedlist>
++                      <listitem><para>Steven J. Hill<email>sjhill@realitydiluted.com</email></para></listitem>
++                      <listitem><para>David Woodhouse<email>dwmw2@infradead.org</email></para></listitem>
++                      <listitem><para>Thomas Gleixner<email>tglx@linutronix.de</email></para></listitem>
++              </orderedlist>
++              A lot of users have provided bugfixes, improvements and helping hands for testing.
++              Thanks a lot.
++      </para>
++      <para>
++              The following people have contributed to this document:
++              <orderedlist>
++                      <listitem><para>Thomas Gleixner<email>tglx@linutronix.de</email></para></listitem>
++              </orderedlist>
++      </para>
++  </chapter>
++</book>
+--- 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 <jamey@crl.dec.com>
++ *  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 <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/poll.h>
++#include <linux/types.h>
++#include <linux/stddef.h>
++#include <linux/timer.h>
++#include <linux/fcntl.h>
++#include <linux/slab.h>
++#include <linux/stat.h>
++#include <linux/proc_fs.h>
++#include <linux/miscdevice.h>
++#include <linux/apm_bios.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/pm.h>
++#include <linux/kernel.h>
++#include <linux/smp_lock.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++
++#ifdef CONFIG_SA1100_H3XXX
++#include <asm/arch/h3600_hal.h>
++#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 <asm/hardware.h>
+-#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 <asm/pgtable.h>
+ #include <asm/mach/map.h>
++#ifdef CONFIG_PXA_RTC_HACK
++#include <asm/setup.h>
++#include <linux/bootmem.h>
++#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
++}
++
++\f
++
++#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 <chri@infis.univ.trieste.it>
++      */
++      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 <cbrake@accelent.com>
++ *
++ * 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 <sjcho@east.isi.edu> &
++ *            Chester Kuo <chester@linux.org.tw>
++ *                    Save more value for the resume function! Support
++ *                    Bitsy/Assabet/Freebird board
++ *
++ * 2001-08-29:        Nicolas Pitre <nico@cam.org>
++ *                    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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pm.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/sysctl.h>
++#include <linux/errno.h>
++#include <linux/cpufreq.h>
++
++#include <asm/hardware.h>
++#include <asm/memory.h>
++#include <asm/system.h>
++#include <asm/leds.h>
++#include <asm/uaccess.h>
++
++
++#ifdef CONFIG_IPAQ_HANDHELD
++#include <asm/arch-sa1100/h3600_asic.h>
++#endif
++
++#define __KERNEL_SYSCALLS__
++#include <linux/unistd.h>
++
++\f
++
++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 <linux/interrupt.h>
+ #include <linux/sysctl.h>
+ #include <linux/errno.h>
++#include <linux/module.h>
+ #include <asm/hardware.h>
+ #include <asm/memory.h>
+@@ -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 <cbrake@accelent.com>
++ *              Initial code
++ *
++ *  2002-10-09: adaptions to ramses
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/pm.h>
++#include <linux/delay.h>
++#ifdef CONFIG_APM
++#include <linux/apm_bios.h>
++#endif
++#define USE_UCB
++//#define PFI_LED
++#define PFI_TURNOFF
++
++#include <asm/types.h>
++#include <asm/setup.h>
++#include <asm/memory.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/irq.h>
++#include <asm/arch-pxa/pxa-regs.h>
++
++#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<MAXLOOPS; i++) {
++              if (state==ST_WAITLOW) {
++                      if (HDQ_GET == 0) {
++                              lastlow = i;
++                              state = ST_LOW;
++                      }
++              } else
++              if (state == ST_LOW) {
++                      if (HDQ_GET) {
++                              // 34 must be changed if the udelay(2) changes!
++                              if (i-lastlow < 34) {
++                                      d = d | mask;
++                              }
++                              if (mask == 0x80) break;
++                              mask = mask << 1;
++                              state = ST_WAITLOW;
++                      }
++              }
++              udelay(2);
++      }
++      if (i==MAXLOOPS) {
++              //printk("no respone after %d\n", i);
++              return -1;
++      } else {
++              //printk("done after %d: %d %x\n", i, d, d);
++              return d;
++      }
++}
++
++
++static int hdq_get_reg_once(unsigned char reg)
++{
++      int d = -1;
++      int i;
++      
++      reg &= 0x7f;
++
++      for (i=0; i<MAXTRIES; i++) {
++              hdq_break();
++              hdq_put_data(reg);
++              d = hdq_get_data();
++              if (d != -1)
++                      break;
++              //printk("hdq_get_reg_once try again: %d\n", i);
++      }
++
++      return d;
++}
++
++/**
++ * The HDQ protocol communication is so bad that we can't really
++ * be sure that we got something usable. So we call hdq_get_reg_once()
++ * twice and compare if we got the same value twice. If not, we try
++ * again. And again, and again ...  up to MAXTRIES times.
++ */
++int ramses_hdq_get_reg(unsigned char reg)
++{
++      int i,d1,d2;
++
++      d1 = hdq_get_reg_once(reg);
++      for (i=0; i<MAXTRIES; i++) {
++              d2 = hdq_get_reg_once(reg);
++              if (d1 == d2)
++                      return d2;
++              d1 = d2;
++      }
++      printk("no response from battery\n");
++      return -1;
++}
++
++
++
++/******************************************************************/
++/*  Power Management                                              */
++/******************************************************************/
++
++#ifdef CONFIG_PM
++static int
++ramses_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
++{
++    static int old_shadow;
++    static int old_ctrl0;
++    static int old_ctrl1;
++    static int old_perval0;
++    static int old_perval1;
++    static int old_duty0;
++    static int old_duty1;
++
++    switch (req) {
++    case PM_SUSPEND:
++      old_shadow  = ramses_control_shadow;
++      old_ctrl0   = PWM_CTRL0;
++      old_ctrl1   = PWM_CTRL1;
++      old_perval0 = PWM_PERVAL0;
++      old_perval1 = PWM_PERVAL1;
++      old_duty0   = PWM_PWDUTY0;
++      old_duty1   = PWM_PWDUTY1;
++
++      // RAMSES_LED_BLUE_OFF();
++      // RAMSES_LED_ORANGE_OFF();
++      RAMSES_UART_OFF();
++      // RAMSES_SCANNER_OFF();
++      // RAMSES_USB_BUS_OFF();
++      // printk("shadow: %08x -> %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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtc.h>
++#include <linux/string.h>
++#include <linux/miscdevice.h>
++#include <linux/proc_fs.h>
++
++#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 <Russ.Dill@asu.edu>
++ * 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 <linux/config.h>
++#include <linux/module.h>
++
++#include <asm/uaccess.h>
++#include <asm/keyboard.h>
++
++/* 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 <ah@doc.ic.ac.uk>
+- *
+- *  03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+- *
+- *  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 <grif@acm.org>
+- * 
+- *  8/97: Fix bug in rs_set_termios with RTS
+- *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+- *
+- *  3/98: Change the IRQ detection, use of probe_irq_o*(),
+- *      suppress TIOCSERGWILD and TIOCSERSWILD
+- *      Etienne Lorrain <etienne.lorrain@ibm.net>
+- *
+- *  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 <stuartm@connecttech.com>
+- *
+- *  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 <alan@linuxcare.com>
+- *
+- *  5/00: Support for the RSA-DV II/S card added.
+- *      Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+- * 
+- *  6/00: Remove old-style timer, use timer_list
+- *        Andrew Morton <andrewm@uow.edu.au>
+- *
+- *  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 <acme@conectiva.com.br>
+- *
+- * 10/00: add in optional software flow control for serial console.
+- *      Kanoj Sarcar <kanoj@sgi.com>  (Modified by Theodore Ts'o)
+- *
+- * 02/02: Fix for AMD Elan bug in transmit irq routine, by
+- *        Christer Weinigel <wingel@hog.ctrl-c.liu.se>,
+- *        Robert Schwebel <robert@schwebel.de>,
+- *        Juergen Beisert <jbeisert@eurodsn.de>,
+- *        Theodore Ts'o <tytso@mit.edu>
+- */
+-
+-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 <linux/config.h>
+ #include <linux/version.h>
+-#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 <linux/ioport.h>
+ #include <linux/mm.h>
+ #include <linux/slab.h>
+-#if (LINUX_VERSION_CODE >= 131343)
+ #include <linux/init.h>
+-#endif
+-#if (LINUX_VERSION_CODE >= 131336)
+ #include <asm/uaccess.h>
+-#endif
+ #include <linux/delay.h>
+ #ifdef CONFIG_SERIAL_CONSOLE
+ #include <linux/console.h>
+ #endif
+-#ifdef ENABLE_SERIAL_PCI
+-#include <linux/pci.h>
+-#endif
+-#ifdef ENABLE_SERIAL_PNP
+-#include <linux/isapnp.h>
+-#endif
+ #ifdef CONFIG_MAGIC_SYSRQ
+ #include <linux/sysrq.h>
+ #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 <asm/system.h>
+ #include <asm/io.h>
+ #include <asm/irq.h>
+ #include <asm/bitops.h>
+-#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<<info->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<<info->iomem_reg_shift));
++              if (show_io) printk("in %02x = %02x\n", offset, value);
++              return value;
+               return readl((unsigned long) info->iomem_base +
+                            (offset<<info->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<<info->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<<info->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 <linux/symtab_begin.h>
+-      X(register_serial),
+-      X(unregister_serial),
+-#include <linux/symtab_end.h>
+-};
+-#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 <pazke@mail.tp.ru>, 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 <arh@01019freenet.de> */
+-      {       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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sysctl.h>
++#include <linux/crc32.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++
++#include <asm/io.h>
++#include <asm/arch/ramses.h>
++#include <asm/uaccess.h>
++
++#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 <h.schurig@mn-logistik.de>");
++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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++
++#include <asm/keyboard.h>
++#include <asm/irq.h>
++#include <linux/keyboard.h>
++
++// 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 <h.schurig@mn-logistik.de>");
++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 <linux/module.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/miscdevice.h>
++#include <linux/input.h>
++
++#include <linux/types.h>
++#include <linux/keyboard.h>
++#include <linux/kd.h>
++
++#include <asm/uaccess.h>
++
++
++// 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<NR_KEYS; i++) {
++              if (map[i] == (c | 0xf000))
++                      return i;
++              if (map[i] == (c | 0xfb00))
++                      return i;
++      }
++
++      return 0;
++}
++
++
++static void wedge_handle_char(int c)
++{
++      int i;
++      unsigned int maps;
++      u_short *map;
++
++      DPRINTK("wedge_handle_char(0x%0x)\n", c);
++
++      for (i=0; i < sizeof(wedge_lookup)/sizeof(wedge_lookup[0]); i++) {
++              if (wedge_lookup[i].c == c) {
++                      ramses_key(wedge_lookup[i].keysym);
++                      return;
++              }
++      }
++
++
++      i = 0;
++      for (maps=0; maps<MAX_NR_KEYMAPS; maps++) {
++              map = key_maps[maps];
++              if (!map)
++                      continue;
++              if ((i = wedge_search_map(map, c))) {
++                      switch(maps) {
++                              case 0:
++                                      break;
++                              case 1:
++                                      i |= 0x500;     // KT_SHIFT from ramses_scancodes.h
++                                      break;
++                              case 2:
++                                      i |= 0x800;     // KT_ALTGR from ramses_scancodes.h
++                                      break;
++                              case 4:
++                                      i |= 0x700;     // KT_CTRL from ramses_scancodes.h
++                                      break;
++                              default:
++                                      DPRINTK("unknown map for char %d %d\n", c, maps);
++                      }
++                      DPRINTK("ramses_key(0x%x)\n", i);
++                      ramses_key(i);
++                      return;
++              }
++      }
++
++      DPRINTK("entry for char %02x missing\n", c);
++}
++
++
++
++static ssize_t wedge_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
++{
++      const char *p = buf;
++      char c;
++
++      //DPRINTK("count=%d\n", count);
++      while (count) {
++              if (copy_from_user(&c, p, sizeof(c)))
++                      return -EFAULT;
++
++              p++;
++              count--;
++
++              wedge_handle_char( (int)c & 0xff);
++
++      }
++      return p - buf;
++}
++
++
++int wedge_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++      static u16 x;
++      static u16 y;
++      static u16 p;
++
++      switch (cmd) {
++      case WEDGE_RAWKEY_DOWN:
++              DPRINTK("send down raw key\n");
++              input_report_key(&ramses_kbd_dev, arg, 1);
++              return 0;
++      case WEDGE_RAWKEY_UP:
++              DPRINTK("send up raw key\n");
++              input_report_key(&ramses_kbd_dev, arg, 0);
++              return 0;
++      case WEDGE_TS_ABS_X:
++              x = arg;
++              return 0;
++      case WEDGE_TS_ABS_Y:
++              y = arg;
++              return 0;
++      case WEDGE_TS_ABS_PRESSURE:
++              p = arg;
++              ucb1x00_ts_evt_add(NULL, arg, y, x);
++              return 0;
++      }
++      return -EINVAL;
++}
++
++
++static struct file_operations wedge_fops = {
++      owner:          THIS_MODULE,
++      write:          wedge_write,
++      open:           wedge_open,
++      release:        wedge_close,
++      ioctl:          wedge_ioctl,
++};
++
++
++static struct miscdevice wedge_miscdev = {
++      minor:  MISC_DYNAMIC_MINOR,
++      name:   "wedge",
++      fops:   &wedge_fops,
++};
++
++static int __init wedge_init(void)
++{
++        int ret;
++      ret = misc_register(&wedge_miscdev);
++      DPRINTK("major,minor is 10,%d\n", wedge_miscdev.minor);
++      return ret;
++}
++
++static void __exit wedge_exit(void)
++{
++      misc_deregister(&wedge_miscdev);
++}
++
++module_init(wedge_init);
++module_exit(wedge_exit);
++
++MODULE_DESCRIPTION("virtual keyboard wedge");
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/misc/Makefile~wedge
++++ linux-2.4.21/drivers/misc/Makefile
+@@ -12,7 +12,7 @@
+ O_TARGET := misc.o
+ export-objs                   := mcp-core.o mcp-sa1100.o mcp-pxa.o \
+-                                 ucb1x00-core.o 
++                                 ucb1x00-core.o ucb1x00-ts.o
+ obj-$(CONFIG_MCP_SA1100)      += mcp-core.o mcp-sa1100.o
+ obj-$(CONFIG_MCP_UCB1200)     += ucb1x00-core.o
+--- linux-2.4.21/drivers/misc/mcp-pxa.c~ucb1x00
++++ linux-2.4.21/drivers/misc/mcp-pxa.c
+@@ -31,6 +31,11 @@
+       return (struct mcp *)codec;
+ }
++void mcp_put(void)
++{
++      pxa_ac97_put();
++}
++
+ void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+ {
+       struct ac97_codec *codec = (struct ac97_codec *)mcp;
+@@ -55,3 +60,7 @@
+ void mcp_disable(struct mcp *mcp)
+ {
+ }
++
++MODULE_AUTHOR("Jeff Sutherland <jeffs@accelent.com>");
++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 <linux/pm.h>
+ #include <linux/tqueue.h>
+ #include <linux/config.h>
++#include <linux/delay.h>
+ #include <asm/irq.h>
+ #include <asm/mach-types.h>
+@@ -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 <asm/dma.h>
+ #include <asm/semaphore.h>
++#include <asm/hardware.h>
+ #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 <rmk@arm.linux.org.uk>");
+ 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 <jonas.holmberg@axis.com>
+  *
+- * $Id$
++ * $Id$
+  *
+  * Copyright (c) 2001 Axis Communications AB
+  *
+@@ -19,6 +19,7 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/flashchip.h>
+@@ -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 <nico@cam.org>
+@@ -21,6 +21,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+@@ -28,21 +29,39 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/xip.h>
+ #include <linux/mtd/map.h>
+-#include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/compatmac.h>
++#include <linux/mtd/cfi.h>
+-// debugging, turns off buffer write mode #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<<i)) 
+                       printk("     - Unknown Bit %X:      supported\n", i);
+       }
+@@ -102,13 +133,171 @@
+       }
+       
+       printk("  Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 
+-             extp->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; i<sizeof(*extp); i++) {
+-                      ((unsigned char *)extp)[i] = 
+-                              cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+-              }
+-              
+-              if (extp->MajorVersion != '1' || 
+-                  (extp->MinorVersion < '0' || extp->MinorVersion > '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<<cfi->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<<cfi->cfiq->DevSize) * cfi->interleave;
+-      mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+       //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
+-      if (!mtd) {
+-              printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+-              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)<reg_sz)){
+-                      *buf=map->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<<extp->FactProtRegSize);
+-      reg_sz=(1<<extp->UserProtRegSize);
+-
+-      return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
+-}
+-
+-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+-{
+-      struct map_info *map = mtd->priv;
+-      struct cfi_private *cfi = map->fldrv_priv;
+-      struct cfi_pri_intelext *extp=cfi->cmdset_priv;
+-      int base_offst,reg_sz;
+-      
+-      /* Check that we actually have some protection registers */
+-      if(!(extp->FeatureSupport&64)){
+-              printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
+-              return 0;
+-      }
+-
+-      base_offst=0;
+-      reg_sz=(1<<extp->FactProtRegSize);
+-
+-      return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
+-}
+-
+-
+-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
++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 (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
+-              i++;
+-
+-      /* As before, drop back one to point at the region in which
+-         the address actually falls
+-      */
+-      i--;
+-      
+-      if ((ofs + len) & (regions[i].erasesize-1))
+-              return -EINVAL;
+-
+-      chipnum = ofs >> cfi->chipshift;
+-      adr = ofs - (chipnum << cfi->chipshift);
+-
+-      i=first;
+-
+-      while(len) {
+-              ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+-              
+-              if (ret)
+-                      return ret;
+-
+-              adr += regions[i].erasesize;
+-              len -= regions[i].erasesize;
+-
+-              if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
+-                      i++;
+-
+-              if (adr >> cfi->chipshift) {
+-                      adr = 0;
+-                      chipnum++;
+-                      
+-                      if (chipnum >= cfi->numchips)
+-                      break;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-
+-static 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<CFIDEV_INTERLEAVE; i++) {
+-                                    chipstatus |= status >> (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<map_words(map); w++) {
++                              for (i = 0; i<cfi_interleave(cfi); i++) {
++                                      chipstatus |= status.x[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 && i<cfi->numchips; 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. <info@crossnet.co.jp>
++ * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
+  *
+  * 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 <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+@@ -23,15 +30,24 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+ #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<<cfi->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<<cfi->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<<cfi->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<<cfi->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 (i<mtd->numeraseregions && (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. <info@crossnet.co.jp> 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 <nico@cam.org>
+  *    - completely revamped method functions so they are aware and
+@@ -17,10 +18,12 @@
+  *    - added a writev function
+  */
++#include <linux/version.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+@@ -30,12 +33,13 @@
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/compatmac.h>
+ static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+-static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs,
++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; i<sizeof(*extp); i++) {
+-                      ((unsigned char *)extp)[i] = 
+-                              cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+-              }
+-              
+-              if (extp->MajorVersion != '1' || 
+-                    (extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
+-                    printk(KERN_WARNING "  Unknown staa Extended Query "
+-                           "version %c.%c.\n",  extp->MajorVersion,
+-                           extp->MinorVersion);
+-                    kfree(extp);
++              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<CFIDEV_INTERLEAVE; i++) {
+-                                    chipstatus |= status >> (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<map_words(map); w++) {
++                              for (i = 0; i<cfi_interleave(cfi); i++) {
++                                      chipstatus |= status.x[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 <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/xip.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/mtd/gen_probe.h>
+@@ -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; i<cfi->numchips; 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <asm/io.h>
++#include <asm/byteorder.h>
++
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/xip.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++#include <linux/mtd/compatmac.h>
++
++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; i<size; i++) {
++              ((unsigned char *)extp)[i] = 
++                      cfi_read_query(map, base+((adr+i)*ofs_factor));
++      }
++
++      /* Make sure it returns to read mode */
++      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);
++
++#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 (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
++              i++;
++
++      /* As before, drop back one to point at the region in which
++         the address actually falls
++      */
++      i--;
++      
++      if ((ofs + len) & (regions[i].erasesize-1))
++              return -EINVAL;
++
++      chipnum = ofs >> cfi->chipshift;
++      adr = ofs - (chipnum << cfi->chipshift);
++
++      i=first;
++
++      while(len) {
++              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 <linux/kernel.h>
+ #include <linux/config.h>
++#include <linux/module.h>
+ #include <linux/kmod.h>
+ #include <linux/spinlock.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/slab.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+-spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED;
++static 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 <dwmw2@infradead.org>");
+--- /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 <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+@@ -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<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
+-           base += (1<<cfi.chipshift))
+-              cp->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 <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
+ #include <linux/mtd/jedec.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+ static struct mtd_info *jedec_probe(struct map_info *);
+ static int jedec_probe8(struct map_info *map,unsigned long base,
+@@ -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)<<chip->addrshift))
+-   #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
++   #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
++   #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
+    
+    unsigned long Time = 0;
+    unsigned long NoTime = 0;
+    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<<chip->addrshift)-1))+((x)<<chip->addrshift))
+-   #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->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<<chip->addrshift)-1))+((x)<<chip->addrshift))
++   #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+    
+-   struct map_info *map = (struct map_info *)mtd->priv;
+-   struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
++   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 <linux/config.h>
+ #include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <asm/io.h>
+@@ -15,7 +18,9 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/mtd/gen_probe.h>
+@@ -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; i<num_erase_regions; i++){
+               p_cfi->cfiq->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; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
+-                      if (cfi->mfr == 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; i<cfi->numchips; 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 <ebrower@resilience.com>
+- * $Id$
++ * $Id$
+  *
+  * This map driver is used to allocate "placeholder" MTD
+  * devices on systems that have socketed/removable media. 
+@@ -23,9 +23,10 @@
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+-
++#include <linux/mtd/compatmac.h>
+ static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -36,10 +37,10 @@
+ static 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 <linux/module.h>
+@@ -11,8 +11,10 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+ static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -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; i<instr->len; i++)
+-              map->write8(map, 0xFF, instr->addr + i);
++      allff = map_word_ff(map);
+-      if (instr->callback)
+-              instr->callback(instr);
++      for (i=0; i<instr->len; 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 <linux/version.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+@@ -12,21 +11,23 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+ 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 <ds@schleef.org>
+  *           2000,2001 Lineo, Inc.
+  *
+- * $Id$
++ * $Id$
+  *
+  * Devices supported:
+  *   LH28F016SCT Symmetrical block flash memory, 2Mx8
+@@ -22,14 +22,15 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/types.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/delay.h>
++#include <linux/init.h>
+ #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=<mtddef>[;<mtddef]
+  * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
+  * <partdef> := <size>[@offset][<name>][ro]
+- * <mtd-id>  := unique id used in mapping driver/device
++ * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
+  * <size>    := standard linux memsize OR "-" to denote all remaining space
+  * <name>    := '(' NAME ')'
+  * 
+@@ -28,7 +28,6 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-#include <asm/setup.h>
+ #include <linux/bootmem.h>
+ /* 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 <mag@sysgo.de>");
+--- 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 <spse@secret.org.uk>
++ *
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/blkdev.h>
++#include <linux/iobuf.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
++#include <linux/list.h>
++#include <linux/mtd/mtd.h>
++
++#ifdef CONFIG_MTD_DEBUG
++#ifdef CONFIG_PROC_FS
++#  include <linux/proc_fs.h>
++#  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 <spse@secret.org.uk>");
++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 <linux/module.h>
+ #include <linux/fs.h>
+ #include <linux/blkdev.h>
+-#include <linux/iobuf.h>
+-#include <linux/slab.h>
++#include <linux/bio.h>
+ #include <linux/pagemap.h>
+ #include <linux/list.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+-#ifdef CONFIG_MTD_DEBUG
+-#ifdef CONFIG_PROC_FS
+-#  include <linux/proc_fs.h>
+-#  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 <spse@secret.org.uk>
++ * Copyright (C) 2004,2005    Jörn Engel <joern@wh.fh-wedel.de>
++ *
++ * Licence: GPL
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/blkdev.h>
++#include <linux/bio.h>
++#include <linux/pagemap.h>
++#include <linux/list.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/buffer_head.h>
++
++#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); p<max; p++) 
++                      if (*p != -1UL) {
++                              lock_page(page);
++                              memset(page_address(page), 0xff, PAGE_SIZE);
++                              set_page_dirty(page);
++                              unlock_page(page);
++                              break;
++                      }
++
++              page_cache_release(page);
++              pages--;
++              index++;
++      }
++      return 0;
++}
++static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++      struct block2mtd_dev *dev = mtd->priv;
++      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=<dev>[,<erasesize>]\"");
++
++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 <spse@secret.org.uk> 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 <dwmw2@infradead.org>
+  *
+- * $Id$
++ * $Id$
+  */
+ #include <linux/kernel.h>
+@@ -19,12 +19,14 @@
+ #include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
++#include <linux/bitops.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/doc2000.h>
+ #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 <dwmw2@infradead.org>
+  *
+- * $Id$
++ * $Id$
+  */
+ #include <linux/kernel.h>
+@@ -19,6 +19,7 @@
+ #include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
++#include <linux/bitops.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+@@ -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 <gerg@snapgear.com>
++ * (c) 2002-2003 SnapGear Inc
++ * (c) 1999 Machine Vision Holdings, Inc.
++ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/bitops.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++
++/* #define ECC_DEBUG */
++
++/* I have no idea why some DoC chips can not use memcop_form|to_io().
++ * This may be due to the different revisions of the ASIC controller built-in or
++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
++ * this:*/
++#undef USE_MEMCPY
++
++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
++              size_t *retlen, u_char *buf);
++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
++              size_t *retlen, const u_char *buf);
++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++              size_t *retlen, u_char *buf, u_char *eccbuf,
++              struct nand_oobinfo *oobsel);
++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++              size_t *retlen, const u_char *buf, u_char *eccbuf,
++              struct nand_oobinfo *oobsel);
++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++              size_t *retlen, u_char *buf);
++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++              size_t *retlen, const u_char *buf);
++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
++
++static struct mtd_info *docmilpluslist = NULL;
++
++
++/* Perform the required delay cycles by writing to the NOP register */
++static void DoC_Delay(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 <kaos@ocs.com.au> 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 <gerg@snapgear.com> 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 <fabrice.bellard@netgem.com>");
+ 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 <dwmw2@infradead.org>                */
+-/* $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 
++   <linux-mtd@lists.infradead.org>.
+ */
+ #define DOC_SINGLE_DRIVER
+@@ -47,18 +45,15 @@
+ #include <linux/module.h>
+ #include <asm/errno.h>
+ #include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
+ /* Where to look for the devices? */
+ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+@@ -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 <abraham@2d3d.co.za>
+  *
+@@ -42,7 +42,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+-#include <linux/version.h>
++#include <linux/init.h>
+ #include <linux/errno.h>
+ #include <linux/mtd/mtd.h>
+ #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 <linux/init.h>
+@@ -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 <macro@ds2.pg.gda.pl>");
++MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
+ 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 <linux/ioport.h>
+ #include <linux/mtd/mtd.h>
++/*
++ * 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 <alex@cendio.se>
+  *
+  * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
+@@ -13,6 +13,8 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+@@ -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 <psionic@psionic.de>
++ * Copyright (c) 2003-2004    Jörn Engel <joern@wh.fh-wedel.de>
++ *
++ * Usage:
++ *
++ * one commend line parameter per device, each in the form:
++ *   phram=<name>,<start>,<len>
++ * <name> may be up to 63 characters.
++ * <start> and <len> 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 <asm/io.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/mtd/mtd.h>
++
++#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=<name>,<start>,<length>\"");
++
++
++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 <joern@wh.fh-wedel.de>");
++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 <linux/version.h>
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+@@ -108,17 +109,11 @@
+ #include <linux/mtd/pmc551.h>
+ #include <linux/mtd/compatmac.h>
+-#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 <linux/mtd/mtd.h>
+ #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 <acme@conectiva.com.br>
+  * - 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 <linux/mtd/blktrans.h>
+ #include <linux/module.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+ /*#define PSYCHO_DEBUG */
+@@ -68,43 +68,13 @@
+ #include <linux/timer.h>
+ #include <linux/major.h>
+ #include <linux/fs.h>
+-#include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/hdreg.h>
+-
+-#if (LINUX_VERSION_CODE >= 0x20100)
+ #include <linux/vmalloc.h>
+-#endif
+-#if (LINUX_VERSION_CODE >= 0x20303)
+ #include <linux/blkpg.h>
+-#endif
++#include <asm/uaccess.h>
+ #include <linux/mtd/ftl.h>
+-/*====================================================================*/
+-/* 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 <linux/blk.h>
+ /*====================================================================*/
+@@ -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 = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
++    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
+     /* Search first megabyte for a valid FTL header */
+     for (offset = 0;
+        (offset + sizeof(header)) < max_offset;
+-       offset += part->mtd->erasesize ? : 0x2000) {
++       offset += part->mbd.mtd->erasesize ? : 0x2000) {
+-      ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, 
++      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 <dwmw2@infradead.org>
++ *
++ * $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 <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/hdreg.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <asm/uaccess.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++
++/*
++ * Maximum number of loops while examining next block, to have a
++ * chance to detect consistency problems (they should never happen
++ * because of the checks done in the mounting.
++ */
++#define MAX_LOOPS 10000
++
++extern void INFTL_dumptables(struct INFTLrecord *inftl);
++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
++
++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
++{
++      struct INFTLrecord *inftl;
++      unsigned long temp;
++
++      if (mtd->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 <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
+--- /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 <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <linux/mtd/compatmac.h>
++
++char inftlmountrev[]="$Revision$";
++
++/*
++ * 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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++#include <linux/mtd/flashchip.h>
+ #include <linux/config.h>
+ #include <linux/pci.h>
+ #include <linux/pci_ids.h>
++#include <linux/list.h>
++
++
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define ADDRESS_NAME_LEN 18
++
++#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
++struct amd76xrom_window {
++      void __iomem *virt;
++      unsigned long phys;
++      unsigned long size;
++      struct list_head maps;
++      struct resource rsrc;
++      struct pci_dev *pdev;
++};
+ struct amd76xrom_map_info {
++      struct list_head list;
+       struct map_info map;
+       struct mtd_info *mtd;
+-      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
++ * <arctic@gibson.dropbear.id.au>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++/*
++ * 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 <arctic@gibson.dropbear.id.au>");
++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 <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/sizes.h>
+ #include <asm/hardware.h>
+@@ -32,80 +33,27 @@
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      __raw_writeb(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      __raw_writew(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      __raw_writel(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      while(len) {
+-              __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+-              from++;
+-              to++;
+-              len--;
+-      }
+-}
+ static struct mtd_info *sram_mtd;
+ struct map_info autcpu12_sram_map = {
+-      name: "SRAM",
+-      size: 32768,
+-      buswidth: 8,
+-      read8: autcpu12_read8,
+-      read16: autcpu12_read16,
+-      read32: autcpu12_read32,
+-      copy_from: autcpu12_copy_from,
+-      write8: autcpu12_write8,
+-      write16: autcpu12_write16,
+-      write32: autcpu12_write32,
+-      copy_to: autcpu12_copy_to
++      .name = "SRAM",
++      .size = 32768,
++      .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 <ben@simtec.co.uk>
++ *
++ * 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/mach-types.h>
++#include <asm/mach/flash.h>
++
++#include <asm/arch/map.h>
++#include <asm/arch/bast-map.h>
++#include <asm/arch/bast-cpld.h>
++
++#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 <ben@simtec.co.uk>");
++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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++#define NAME     "Beech Linux Flash"
++#define PADDR    BEECH_BIGFLASH_PADDR
++#define SIZE     BEECH_BIGFLASH_SIZE
++#define BUSWIDTH 1
++
++/* Flash memories on these boards are memory resources, accessed big-endian. */
++
++
++static struct map_info beech_mtd_map = {
++      .name =         NAME,
++      .size =         SIZE,
++      .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 <bcbrock@us.ibm.com>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/arch/hardware.h>
+ #include <linux/mtd/mtd.h>
+@@ -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 <nico@cam.org>
+  *
+- * $Id$
++ * $Id$
+  */
+ #include <linux/config.h>
+@@ -19,6 +19,7 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -31,62 +32,10 @@
+ #include <asm/sizes.h>
+ /*
+- * This isnt complete yet, so...
++ * This isn't complete yet, so...
+  */
+ #define CONFIG_MTD_CEIVA_STATICMAP
+-static __u8 clps_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 clps_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 clps_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-static struct map_info clps_map __initdata = {
+-      name:           "clps flash",
+-      read8:          clps_read8,
+-      read16:         clps_read16,
+-      read32:         clps_read32,
+-      copy_from:      clps_copy_from,
+-      write8:         clps_write8,
+-      write16:        clps_write16,
+-      write32:        clps_write32,
+-      copy_to:        clps_copy_to,
+-};
+-
+ #ifdef CONFIG_MTD_CEIVA_STATICMAP
+ /*
+  * See include/linux/mtd/partitions.h for definition of the mtd_partition
+@@ -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 <kd@flaga.is>
+  *
+- *  $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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -55,83 +56,33 @@
+ #define FLASH_PARTITION3_ADDR 0x00240000
+ #define FLASH_PARTITION3_SIZE 0x001C0000
+-__u8 flagadm_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 flagadm_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 flagadm_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      __raw_writeb(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      __raw_writew(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      __raw_writel(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+ struct map_info flagadm_map = {
+-              name: "FlagaDM flash device",
+-              size: FLASH_SIZE,
+-              buswidth: 2,
+-              read8: flagadm_read8,
+-              read16: flagadm_read16,
+-              read32: flagadm_read32,
+-              copy_from: flagadm_copy_from,
+-              write8: flagadm_write8,
+-              write16: flagadm_write16,
+-              write32: flagadm_write32,
+-              copy_to: flagadm_copy_to
++              .name =         "FlagaDM flash device",
++              .size =         FLASH_SIZE,
++              .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: <source@mvista.com>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <platforms/chestnut.h>
++
++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("<source@mvista.com>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
+-
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #include <linux/delay.h>
+-#endif
+-
+-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #define CC_GCR             0xB4013818
+@@ -97,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;i<PHYSMAP_NUMBER;i++) {
+-              printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
+-                memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info));
+-              cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
+-              if (!cstm_mips_ixx_map[i].map_priv_1) {
++              printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n", 
++                     cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
++
++
++              cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr;
++              cstm_mips_ixx_map[i].virt = ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
++              if (!cstm_mips_ixx_map[i].virt) {
+                       printk(KERN_WARNING "Failed to ioremap\n");
+                       return -EIO;
+               }
+               cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
+               cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
+-              cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
+-              //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1));
++              cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth;
++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++                cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
++#endif
++              simple_map_init(&cstm_mips_ixx_map[i]);
++              //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
+       }
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+@@ -244,7 +200,7 @@
+                       printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
+               }
+               if (mymtd) {
+-                      mymtd->module = THIS_MODULE;
++                      mymtd->owner = THIS_MODULE;
+                       cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd;
+                       add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions);
+@@ -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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
++#include <linux/errno.h>
+ /* 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 <kd@flaga.is>");
+-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board");
++MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
+--- 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 <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -18,143 +20,199 @@
+ #include <asm/io.h>
+ #include <asm/hardware/dec21285.h>
++#include <asm/mach-types.h>
+-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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -36,7 +37,7 @@
+ #include <linux/mtd/concat.h>
+ /*
+-** 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/errno.h>
++
++/*
++ * 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 <scott.wood@timesys.com>");
++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 <mporter@kernel.crashing.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++#include <linux/version.h>
++#include <asm/io.h>
++#include <asm/ibm44x.h>
++#include <platforms/4xx/ebony.h>
++
++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 <mporter@kernel.crashing.org>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <nico@cam.org>
++ *  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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#ifdef CONFIG_EPXA10DB
++#define BOARD_NAME "EPXA10DB"
++#else
++#define BOARD_NAME "EPXA1DB"
++#endif
++
++static int nr_parts = 0;
++static struct mtd_partition *parts;
++
++static struct mtd_info *mymtd;
++
++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; i<mymtd->numeraseregions;i++){
++                      int j;
++                      for(j=0;j<mymtd->eraseregions[i].numblocks;j++){
++                              mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize);
++                      }
++              }
++      }
++
++#ifdef CONFIG_MTD_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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -23,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 <jungjun.kim@hynix.com>
++ *     2003 Thomas Gleixner <tglx@linutronix.de>      
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++static struct mtd_info *mymtd;
++
++static struct map_info h720x_map = {
++      .name =         "H720X",
++      .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 <tglx@linutronix.de>");
++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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++#include <linux/mtd/flashchip.h>
++#include <linux/config.h>
++#include <linux/pci.h>
++#include <linux/pci_ids.h>
++#include <linux/list.h>
++
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define ADDRESS_NAME_LEN 18
++
++#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
++
++#define BIOS_CNTL     0x4e
++#define FWH_DEC_EN1   0xE3
++#define FWH_DEC_EN2   0xF0
++#define FWH_SEL1      0xE8
++#define FWH_SEL2      0xEE
++
++struct ichxrom_window {
++      void __iomem* virt;
++      unsigned long phys;
++      unsigned long size;
++      struct list_head maps;
++      struct resource rsrc;
++      struct pci_dev *pdev;
++};
++
++struct ichxrom_map_info {
++      struct list_head list;
++      struct map_info map;
++      struct mtd_info *mtd;
++      struct resource rsrc;
++      char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
++};
++
++static struct ichxrom_window ichxrom_window = {
++      .maps = LIST_HEAD_INIT(ichxrom_window.maps),
++};
++
++static void ichxrom_cleanup(struct ichxrom_window *window)
++{
++      struct ichxrom_map_info *map, *scratch;
++      u16 word;
++
++      /* Disable writes through the rom window */
++      pci_read_config_word(window->pdev, BIOS_CNTL, &word);
++      pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
++
++      /* 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 <ebiederman@lnxi.com>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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; i<NUM_FLASHBANKS; i++)
+       {
+               printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n",
+                      pt[i].size, pt[i].addr);
+-              impa7_map[i].map_priv_1 = (unsigned long)
+-                ioremap(pt[i].addr, pt[i].size);
+-              if (!impa7_map[i].map_priv_1) {
++              impa7_map[i].phys = pt[i].addr;
++              impa7_map[i].virt = ioremap(pt[i].addr, pt[i].size);
++              if (!impa7_map[i].virt) {
+                       printk(MSG_PREFIX "failed to ioremap\n");
+                       return -EIO;
+               }
++              simple_map_init(&impa7_map[i]);
+               impa7_mtd[i] = 0;
+               type = rom_probe_types;
+@@ -167,43 +104,34 @@
+                       impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
+               }
+-              if (impa7_mtd[i]) 
+-              {
+-                      impa7_mtd[i]->module = THIS_MODULE;
+-                      add_mtd_device(impa7_mtd[i]);
++              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<NUM_FLASHBANKS; i++) 
+-      {
+-              if (impa7_mtd[i]) 
+-              {
++      for (i=0; i<NUM_FLASHBANKS; i++) {
++              if (impa7_mtd[i]) {
++#ifdef CONFIG_MTD_PARTITIONS
++                      del_mtd_partitions(impa7_mtd[i]);
++#else
+                       del_mtd_device(impa7_mtd[i]);
++#endif
+                       map_destroy(impa7_mtd[i]);
+-              }
+-              if (impa7_map[i].map_priv_1)
+-              {
+-                      iounmap((void *)impa7_map[i].map_priv_1);
+-                      impa7_map[i].map_priv_1 = 0;
++                      iounmap((void *)impa7_map[i].virt);
++                      impa7_map[i].virt = 0;
+               }
+       }
+ }
+--- /dev/null
++++ linux-2.4.21/drivers/mtd/maps/integrator-flash-v24.c
+@@ -0,0 +1,258 @@
++/*======================================================================
++
++    drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
++  
++    Copyright (C) 2000 ARM Limited
++  
++   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
++  
++   This is access code for flashes using ARM's flash partitioning 
++   standards.
++
++   $Id$
++
++======================================================================*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/system.h>
++
++// 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 <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/device.h>
+ #include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
++#include <asm/mach/flash.h>
+ #include <asm/hardware.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **);
+-
+-// board specific stuff - sorry, it should be in arch/arm/mach-*.
+-#ifdef CONFIG_ARCH_INTEGRATOR
+-
+-#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 <nico@cam.org>
++ * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
++ * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
++ * 
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/spinlock.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <asm/page.h>
++#include <asm/mach-types.h>
++#include <asm/system.h>
++#include <asm/errno.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#ifdef CONFIG_MTD_CONCAT
++#include <linux/mtd/concat.h>
++#endif
++
++#include <asm/hardware.h>
++#include <asm/arch-sa1100/h3600.h>
++#include <asm/io.h>
++
++
++#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; i<MAX_IPAQ_CS; i++)
++                      ipaq_map[i].bankwidth = 2;
++      else
++              for(i=0; i<MAX_IPAQ_CS; i++)
++                      ipaq_map[i].bankwidth = 4;
++                      
++      /*
++       * Static partition definition selection
++       */
++      part_type = "static";
++
++      simple_map_init(&ipaq_map[0]);
++      simple_map_init(&ipaq_map[1]);
++
++#ifdef CONFIG_IPAQ_HANDHELD
++      if (machine_is_ipaq()) {
++              parts = h3xxx_partitions;
++              nb_parts = ARRAY_SIZE(h3xxx_partitions);
++              for(i=0; i<MAX_IPAQ_CS; i++) {
++                      ipaq_map[i].size = h3xxx_max_flash_size;
++                      ipaq_map[i].set_vpp = h3xxx_set_vpp;
++                      ipaq_map[i].phys = cs_phys[i];
++                      ipaq_map[i].virt = __ioremap(cs_phys[i], 0x04000000, 0, 1);
++                      if (machine_is_h3100 () || machine_is_h1900())
++                              ipaq_map[i].bankwidth = 2;
++              }
++              if (machine_is_h3600()) {
++                      /* No asset partition here */
++                      h3xxx_partitions[1].size += 0x40000;
++                      nb_parts--;
++              }
++      }
++#endif
++#ifdef CONFIG_ARCH_H5400
++      if (machine_is_h5400()) {
++              ipaq_map[0].size = 0x02000000;
++              ipaq_map[1].size = 0x02000000;
++              ipaq_map[1].phys = 0x02000000;
++              ipaq_map[1].virt = ipaq_map[0].virt + 0x02000000;
++      }
++#endif
++#ifdef CONFIG_ARCH_H1900
++      if (machine_is_h1900()) {
++              ipaq_map[0].size = 0x00400000;
++              ipaq_map[1].size = 0x02000000;
++              ipaq_map[1].phys = 0x00080000;
++              ipaq_map[1].virt = ipaq_map[0].virt + 0x00080000;
++      }
++#endif
++
++#ifdef CONFIG_SA1100_JORNADA56X
++      if (machine_is_jornada56x()) {
++              parts = jornada_partitions;
++              nb_parts = ARRAY_SIZE(jornada_partitions);
++              ipaq_map[0].size = jornada_max_flash_size;
++              ipaq_map[0].set_vpp = jornada56x_set_vpp;
++              ipaq_map[0].virt = (__u32)__ioremap(0x0, 0x04000000, 0, 1);
++      }
++#endif
++#ifdef CONFIG_SA1100_JORNADA720
++      if (machine_is_jornada720()) {
++              parts = jornada_partitions;
++              nb_parts = ARRAY_SIZE(jornada_partitions);
++              ipaq_map[0].size = jornada_max_flash_size;
++              ipaq_map[0].set_vpp = jornada720_set_vpp;
++      }
++#endif
++
++
++      if (machine_is_ipaq()) { /* for iPAQs only */
++              for(i=0; i<MAX_IPAQ_CS; i++) {
++                      printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with CFI.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
++                      my_sub_mtd[i] = do_map_probe("cfi_probe", &ipaq_map[i]);
++                      if (!my_sub_mtd[i]) {
++                              printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
++                              my_sub_mtd[i] = do_map_probe("jedec_probe", &ipaq_map[i]);
++                      }
++                      if (!my_sub_mtd[i]) {
++                              printk(KERN_NOTICE "iPAQ flash: failed to find flash.\n");
++                              if (i)
++                                      break;
++                              else
++                                      return -ENXIO;
++                      } else
++                              printk(KERN_NOTICE "iPAQ flash: found %d bytes\n", my_sub_mtd[i]->size);
++                      
++                      /* 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<MAX_IPAQ_CS; i++) 
++#else
++                      for(i=1; i<MAX_IPAQ_CS; i++) 
++#endif                  
++                      {
++                              if (my_sub_mtd[i])
++                                      map_destroy(my_sub_mtd[i]);
++                      }
++              if (parsed_parts)
++                      kfree(parsed_parts);
++      }
++}
++
++static int __init h1900_special_case(void)
++{
++      /* The iPAQ h1900 is a special case - it has weird ROM. */
++      simple_map_init(&ipaq_map[0]);
++      ipaq_map[0].size = 0x80000;
++      ipaq_map[0].set_vpp = h3xxx_set_vpp;
++      ipaq_map[0].phys = 0x0;
++      ipaq_map[0].virt = __ioremap(0x0, 0x04000000, 0, 1);
++      ipaq_map[0].bankwidth = 2;
++      
++      printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
++      mymtd = do_map_probe("jedec_probe", &ipaq_map[0]);
++      if (!mymtd)
++              return -ENODEV;
++      add_mtd_device(mymtd);
++      printk(KERN_NOTICE "iPAQ flash: registered h1910 flash\n");
++      
++      return 0;
++}
++
++module_init(ipaq_mtd_init);
++module_exit(ipaq_mtd_cleanup);
++
++MODULE_AUTHOR("Jamey Hicks");
++MODULE_DESCRIPTION("IPAQ CFI map driver");
++MODULE_LICENSE("MIT");
+--- linux-2.4.21/drivers/mtd/maps/iq80310.c~mtd-cvs
++++ linux-2.4.21/drivers/mtd/maps/iq80310.c
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+  *
+  * Mapping for the Intel XScale IQ80310 evaluation board
+  *
+@@ -14,6 +14,8 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -26,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 <naeem.m.afzal@intel.com>
++ * Maintainer: Deepak Saxena <dsaxena@plexity.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ * 
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/mach/flash.h>
++
++#include <linux/reboot.h>
++
++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 <dsaxena@plexity.net>");
++
+--- /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 <dsaxena@mvista.com>
++ *
++ * Copyright (C) 2002 Intel Corporation
++ * Copyright (C) 2003-2004 MontaVista Software, Inc.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++#include <asm/mach/flash.h>
++
++#include <linux/reboot.h>
++
++#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 <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <brian@murphy.dk> or whoever he
+- * works for.
++ * (C) 2002 Brian Murphy <brian@murphy.dk>
+  *
+  * 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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
+ #include <asm/lasat/lasat.h>
+-#include <asm/lasat/lasat_mtd.h>
+-
+-static struct mtd_info *mymtd;
+-
+-static __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 <brian@murphy.dk>");
+--- /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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/dma-mapping.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/lubbock.h>
++
++
++#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 <nico@cam.org>");
++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 <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/mtd/map.h>
++
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++
++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 <ksaito@interface.co.jp>");
++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 <joern@wh.fh-wedelde>
++ */
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++
++
++#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; i<NO_DEVICES; i++) {
++              struct map_info *map = &maps[i];
++              if (!map->virt)
++                      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<NO_DEVICES; i++)
++              map_one(&maps[i]);
++      up(&map_mutex);
++      return 0;
++}
++
++
++static void __exit mphysmap_exit(void)
++{
++      int i;
++      down(&map_mutex);
++      for (i=0; i<NO_DEVICES; i++)
++              unmap_one(&maps[i]);
++      up(&map_mutex);
++}
++
++
++__module_param_call("", mphysmap, mphysmap_setup, NULL, NULL, 0600);
++
++module_init(mphysmap_init);
++module_exit(mphysmap_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedelde>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -50,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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -20,47 +21,23 @@
+ #define NVRAM_WINDOW_SIZE 0x00007FF0
+ #define NVRAM_BUSWIDTH 1
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+ static unsigned int cacheflush = 0;
+ static struct mtd_info *flash_mtd;
+ static struct mtd_info *nvram_mtd;
+-__u8 ocelot_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      cacheflush = 1;
+-      __raw_writeb(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      if (cacheflush) {
+-              dma_cache_inv(map->map_priv_2, map->size);
+-              cacheflush = 0;
+-      }
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+ {
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++        struct map_info *map = 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 <mporter@kernel.crashing.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++#include <linux/version.h>
++#include <asm/io.h>
++#include <asm/ibm44x.h>
++#include <platforms/4xx/ocotea.h>
++
++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 <mporter@kernel.crashing.org>");
++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 <asm/io.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/errno.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++
++#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 <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/timer.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+@@ -24,6 +25,7 @@
+ #include <pcmcia/ds.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #ifdef CONFIG_MTD_DEBUG
+ static int debug = CONFIG_MTD_DEBUG_VERBOSE;
+@@ -47,7 +49,7 @@
+ #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 <spse@secret.org.uk>");
+ MODULE_DESCRIPTION(DRIVER_DESC);
+-MODULE_PARM(buswidth, "i");
+-MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)");
++MODULE_PARM(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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/config.h>
+-
+-#ifdef CONFIG_MTD_PARTITIONS
+ #include <linux/mtd/partitions.h>
+-#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 <ben@simtec.co.uk>
++ *
++ * 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/plat-ram.h>
++
++#include <asm/io.h>
++
++/* private structure for each mtd platform ram device created */
++
++struct platram_info {
++      struct device           *dev;
++      struct mtd_info         *mtd;
++      struct map_info          map;
++      struct 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 <ben@simtec.co.uk>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -24,58 +25,13 @@
+  * MAP DRIVER STUFF
+  */
+-__u8 pnc_read8(struct map_info *map, unsigned long ofs)
+-{
+-  return *(__u8 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u16 pnc_read16(struct map_info *map, unsigned long ofs)
+-{
+-  return *(__u16 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u32 pnc_read32(struct map_info *map, unsigned long ofs)
+-{
+-  return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
+-}
+-
+-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-  memcpy(to, (void *)(WINDOW_ADDR + from), len);
+-}
+-
+-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-  *(__u8 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-  *(__u16 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-  *(__u32 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-  memcpy((void *)(WINDOW_ADDR + to), from, len);
+-}
+-struct map_info pnc_map = {
+-      name: "PNC-2000",
+-      size: WINDOW_SIZE,
+-      buswidth: 4,
+-      read8: pnc_read8,
+-      read16: pnc_read16,
+-      read32: pnc_read32,
+-      copy_from: pnc_copy_from,
+-      write8: pnc_write8,
+-      write16: pnc_write16,
+-      write32: pnc_write32,
+-      copy_to: pnc_copy_to
++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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#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. <source@mvista.com>
+  *
++ * 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 <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -40,96 +25,102 @@
+ #include <asm/io.h>
++#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 <source@mvista.com>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -17,80 +18,31 @@
+ static struct mtd_info *mymtd;
+-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      __raw_writeb(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      __raw_writew(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      __raw_writel(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-struct map_info rpxlite_map = {
+-      name: "RPX",
+-      size: WINDOW_SIZE,
+-      buswidth: 4,
+-      read8: rpxlite_read8,
+-      read16: rpxlite_read16,
+-      read32: rpxlite_read32,
+-      copy_from: rpxlite_copy_from,
+-      write8: rpxlite_write8,
+-      write16: rpxlite_write16,
+-      write32: rpxlite_write32,
+-      copy_to: rpxlite_copy_to
++static struct map_info rpxlite_map = {
++      .name = "RPX",
++      .size = WINDOW_SIZE,
++      .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 <nico@cam.org>
+  * 
+- * $Id$
++ * $Id$
+  */
+ #include <linux/config.h>
+@@ -11,330 +11,212 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/concat.h>
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+ #include <asm/io.h>
++#include <asm/sizes.h>
++#include <asm/arch/h3600.h>
+ #ifndef CONFIG_ARCH_SA1100
+ #error This is for SA1100 architecture only
+ #endif
++/*
++ * This isnt complete yet, so...
++ */
++#define CONFIG_MTD_SA1100_STATICMAP 1
+-#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)
++ *
++ * <or>
++ *
++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
++ *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
++ *   One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
+  */
+-#define BADGE4_FLASH_SIZE             0x00400000
+ static struct mtd_partition badge4_partitions[] = {
+       {
+-              name:           "BLOB boot loader",
+-              offset:         0,
+-              size:           0x0000A000
+-      }, {
+-              name:           "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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#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 <carolyn.smith@tektronix.com>");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <thockin@sun.com>
+  *
+@@ -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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <wingel@nano-system.com>
+-   $Id$ 
++   $Id$ 
+    National Semiconductor SCx200 flash mapped with DOCCS
+ */
+@@ -11,6 +11,7 @@
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#define WINDOW_ADDR 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 <AKC@pel.dk>)");
++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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
+-
+-
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-__u32 soleng_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      __raw_writel(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
++#include <linux/errno.h>
+ 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 <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/fs.h>
+ #include <linux/errno.h>
+ #include <linux/init.h>
+@@ -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 <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+@@ -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 <sean@mess.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
++ *
++ * Note:
++ * - In order for detection to work, jumper 3 must be set.
++ * - Drive A and B use 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#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 <sean@mess.org>");
++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 <asm/io.h>
+ #include <asm/core_tsunami.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #define FLASH_ENABLE_PORT 0x00C00001
+ #define FLASH_ENABLE_BYTE 0x01
+ #define FLASH_DISABLE_BYTE 0x00
+ #define MAX_TIG_FLASH_SIZE (12*1024*1024)
+-static inline  __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset)
++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 <linux/kernel.h>
+ #include <linux/fs.h>
+ #include <linux/major.h>
++#include <linux/root_dev.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+@@ -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 <asm/io.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #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 <holindho@infradead.org>
++ * 
++ * 
++ * This program is free software; you can redistribute  it and/or modify it
++ * under  the terms of  the GNU General  Public License as published by the
++ * Free Software Foundation;  either version 2 of the  License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++#include <linux/version.h>
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++#include <platforms/4xx/walnut.h>
++
++/* 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 <holindho@infradead.org>");
++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 <dwmw2@infradead.org>
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/config.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/immap_cpm2.h>
++
++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 <dwmw2@infradead.org>");
++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 <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.4 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++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(&current->sigmask_lock);
++      sigfillset(&current->blocked);
++      recalc_sigpending();
++      spin_unlock_irq(&current->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<<tr->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<<tr->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;
++      }
++
++      /*
++        <viro_zzz> dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices
++        <viro> 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<<tr->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; i<MAX_MTD_DEVICES; i++) {
++              if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++                      tr->add_mtd(tr, mtd_table[i]);
++      }
++      up(&mtd_table_mutex);
++
++      return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++      struct list_head *this, *next;
++
++      down(&mtd_table_mutex);
++
++      /* Clean up the kernel thread */
++      tr->blkcore_priv->exiting = 1;
++      wake_up(&tr->blkcore_priv->thread_wq);
++      wait_for_completion(&tr->blkcore_priv->thread_dead);
++      
++      /* Remove it from the list of active majors */
++      list_del(&tr->list);
++
++      /* 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 <dwmw2@infradead.org>");
++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 <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.5 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <linux/devfs_fs_kernel.h>
++
++static LIST_HEAD(blktrans_majors);
++
++extern struct semaphore mtd_table_mutex;
++extern struct mtd_info *mtd_table[];
++
++struct mtd_blkcore_priv {
++      struct completion thread_dead;
++      int exiting;
++      wait_queue_head_t thread_wq;
++      struct request_queue *rq;
++      spinlock_t queue_lock;
++};
++
++static int do_blktrans_request(struct mtd_blktrans_ops *tr,
++                             struct mtd_blktrans_dev *dev,
++                             struct request *req)
++{
++      unsigned long block, nsect;
++      char *buf;
++
++      block = req->sector;
++      nsect = req->current_nr_sectors;
++      buf = req->buffer;
++
++      if (!(req->flags & REQ_CMD))
++              return 0;
++
++      if (block + nsect > get_capacity(req->rq_disk))
++              return 0;
++
++      switch(rq_data_dir(req)) {
++      case READ:
++              for (; nsect > 0; nsect--, block++, buf += 512)
++                      if (tr->readsect(dev, block, buf))
++                              return 0;
++              return 1;
++
++      case WRITE:
++              if (!tr->writesect)
++                      return 0;
++
++              for (; nsect > 0; nsect--, block++, buf += 512)
++                      if (tr->writesect(dev, block, buf))
++                              return 0;
++              return 1;
++
++      default:
++              printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
++              return 0;
++      }
++}
++
++static int mtd_blktrans_thread(void *arg)
++{
++      struct mtd_blktrans_ops *tr = arg;
++      struct request_queue *rq = tr->blkcore_priv->rq;
++
++      /* we might get involved when memory gets low, so use PF_MEMALLOC */
++      current->flags |= PF_MEMALLOC | 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(&current->sighand->siglock);
++      sigfillset(&current->blocked);
++      recalc_sigpending();
++      spin_unlock_irq(&current->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; i<MAX_MTD_DEVICES; i++) {
++              if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++                      tr->add_mtd(tr, mtd_table[i]);
++      }
++
++      up(&mtd_table_mutex);
++
++      return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++      struct list_head *this, *next;
++
++      down(&mtd_table_mutex);
++
++      /* Clean up the kernel thread */
++      tr->blkcore_priv->exiting = 1;
++      wake_up(&tr->blkcore_priv->thread_wq);
++      wait_for_completion(&tr->blkcore_priv->thread_dead);
++
++      /* Remove it from the list of active majors */
++      list_del(&tr->list);
++
++      list_for_each_safe(this, next, &tr->devs) {
++              struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
++              tr->remove_dev(dev);
++      }
++
++      devfs_remove(tr->name);
++      blk_cleanup_queue(tr->blkcore_priv->rq);
++      unregister_blkdev(tr->major, tr->name);
++
++      up(&mtd_table_mutex);
++
++      kfree(tr->blkcore_priv);
++
++      if (!list_empty(&tr->devs))
++              BUG();
++      return 0;
++}
++
++static void __exit mtd_blktrans_exit(void)
++{
++      /* No race here -- if someone's currently in register_mtd_blktrans
++         we're screwed anyway. */
++      if (blktrans_notifier.list.next)
++              unregister_mtd_user(&blktrans_notifier);
++}
++
++module_exit(mtd_blktrans_exit);
++
++EXPORT_SYMBOL_GPL(register_mtd_blktrans);
++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
+--- 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 <nico@cam.org>
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
+  */
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
++#include <linux/vmalloc.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#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 <linux/blk.h>
+-/* 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 <linux/devfs_fs_kernel.h>
+-static void mtd_notify_add(struct mtd_info* mtd);
+-static void mtd_notify_remove(struct mtd_info* mtd);
+-static struct mtd_notifier notifier = {
+-        mtd_notify_add,
+-        mtd_notify_remove,
+-        NULL
+-};
+-static devfs_handle_t devfs_dir_handle = NULL;
+-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
+-#endif
++#include <linux/mtd/blktrans.h>
+ static struct mtdblk_dev {
+-      struct mtd_info *mtd; /* Locked */
++      struct mtd_info *mtd;
+       int count;
+       struct semaphore cache_sem;
+       unsigned char *cache_data;
+@@ -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(&notifier);
+-#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(&notifier);
+-      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 <dwmw2@infradead.org>
++ *
++ * Simple read-only (writable only for RAM) mtdblock driver
+  */
+-#ifdef MTDBLOCK_DEBUG
+-#define DEBUGLVL debug
+-#endif                                                               
+-
+-
+-#include <linux/module.h>
+-#include <linux/types.h>
+-
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#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 <linux/blk.h>
+-
+-#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 <linux/mtd/blktrans.h>
+-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 <eauth@softsys.co.at> et al.");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+ 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 <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <asm/uaccess.h>
+ #ifdef CONFIG_DEVFS_FS
+ #include <linux/devfs_fs_kernel.h>
++#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(&notifier);
++#else
++      devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
++
++      register_mtd_user(&notifier);
++#endif
++}
++
++static inline void mtdchar_devfs_exit(void)
++{
++      unregister_mtd_user(&notifier);
++      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(&notifier);
+-#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(&notifier);
+-      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 <rkaiser@sysgo.de>
+  *
++ * NAND support by Christian Gan <cgan@iders.ca>
++ *
+  * This code is GPL
+  *
+- * $Id$
++ * $Id$
+  */
+ #include <linux/module.h>
+@@ -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 <rkaiser@sysgo.de>");
+ 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 <linux/major.h>
+ #include <linux/fs.h>
+ #include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #ifdef CONFIG_PROC_FS
+ #include <linux/proc_fs.h>
+@@ -24,9 +25,15 @@
+ #include <linux/mtd/mtd.h>
+-static DECLARE_MUTEX(mtd_table_mutex);
+-static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
+-static struct mtd_notifier *mtd_notifiers = NULL;
++/* These are exported solely for the purpose of mtd_blkdevs.c. You 
++   should not use them for _anything_ else */
++DECLARE_MUTEX(mtd_table_mutex);
++struct mtd_info *mtd_table[MAX_MTD_DEVICES];
++
++EXPORT_SYMBOL_GPL(mtd_table_mutex);
++EXPORT_SYMBOL_GPL(mtd_table);
++
++static LIST_HEAD(mtd_notifiers);
+ /**
+  *    add_mtd_device - register an MTD device
+@@ -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 <gleixner@autronix.de>
+  *                    added support for read_oob, write_oob
+@@ -16,10 +16,11 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/list.h>
+-
++#include <linux/config.h>
++#include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-
++#include <linux/mtd/compatmac.h>
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+@@ -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 <nico@cam.org>");
+--- 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 <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++
++/* fixme: this is ugly */
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
++#include <asm/mach-au1x00/au1000.h>
++#ifdef CONFIG_MIPS_PB1550
++#include <asm/mach-pb1x00/pb1550.h> 
++#endif
++#ifdef CONFIG_MIPS_DB1550
++#include <asm/mach-db1x00/db1x00.h> 
++#endif
++#else
++#include <asm/au1000.h>
++#ifdef CONFIG_MIPS_PB1550
++#include <asm/pb1550.h> 
++#endif
++#ifdef CONFIG_MIPS_DB1550
++#include <asm/db1x00.h> 
++#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; i<len; i++) {
++              writeb(buf[i], this->IO_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; i<len; i++) {
++              buf[i] = readb(this->IO_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; i<len; i++) {
++              if (buf[i] != readb(this->IO_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; i<len; i++) {
++              writew(p[i], this->IO_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; i<len; i++) {
++              p[i] = readw(this->IO_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; i<len; i++) {
++              if (p[i] != readw(this->IO_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 <tgxl@linutronix.de>
+  *
+  *  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 <linux/version.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+@@ -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 <dan_brown@ieee.org>
++ * (C) 2004 Kalev Lember <kalev@smartlink.ee>
++ *
++ * Author: David Woodhouse <dwmw2@infradead.org>
++ * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
++ * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
++ * 
++ * 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 <tglx@linutronix.de>
++ *  
++ * Interface to generic NAND code for M-Systems DiskOnChip devices
++ *
++ * $Id$
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/rslib.h>
++#include <linux/moduleparam.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/inftl.h>
++
++/* 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 <dwmw2@infradead.org>");
++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 <linux/slab.h>
+ #include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/partitions.h>
+@@ -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 <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
++#include <asm/sizes.h>
++#include <asm/arch/h1900-gpio.h>
++#include <asm/arch/ipaq.h>
++
++/*
++ * 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 <joshua at joshuawise dot com>");
++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 <ben-mtd@fluff.org>
++ *
++ *  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 <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/compatmac.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <asm/io.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#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; i<len; i++)
++              writeb(buf[i], this->IO_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; i<len; i++)
++              buf[i] = readb(this->IO_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; i<len; i++)
++              if (buf[i] != readb(this->IO_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; i<len; i++)
++              writew(p[i], this->IO_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; i<len; i++)
++              p[i] = readw(this->IO_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; i<len; i++)
++              if (p[i] != readw(this->IO_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 <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
++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 <linux/slab.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/compatmac.h>
++#include <linux/bitops.h>
++#include <linux/delay.h>
++
++
++/** 
++ * 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 <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
++#include <linux/mtd/nand_ecc.h>
+ /*
+  * 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 <sjhill@cotw.com>");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+ 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 <linux/module.h>
+ #include <linux/mtd/nand.h>
+-
+ /*
+ *     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 <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
++ *
++ * 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 <linux/config.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/vmalloc.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#ifdef CONFIG_NS_ABS_POS
++#include <asm/io.h>
++#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], <address input>
++ * 
++ * 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 <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <platforms/PPChameleonEVB.h>
++
++#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 <support-ppchameleon@dave-tech.it>");
++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 <linux/delay.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/rslib.h>
++#include <linux/module.h>
++#include <linux/mtd/compatmac.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++
++/*
++ * 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 <dmarlin@redhat.com");
++MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
++
+--- /dev/null
++++ linux-2.4.21/drivers/mtd/nand/s3c2410.c
+@@ -0,0 +1,706 @@
++/* linux/drivers/mtd/nand/s3c2410.c
++ *
++ * Copyright (c) 2004 Simtec Electronics
++ *    http://www.simtec.co.uk/products/SWLINUX/
++ *    Ben Dooks <ben@simtec.co.uk>
++ *
++ * 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 <config/mtd/nand/s3c2410/hwecc.h>
++#include <config/mtd/nand/s3c2410/debug.h>
++
++#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
++#define DEBUG
++#endif
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/mach-types.h>
++#include <asm/hardware/clock.h>
++
++#include <asm/arch/regs-nand.h>
++#include <asm/arch/nand.h>
++
++#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 <ben@simtec.co.uk>");
++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 <linux/genhd.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <linux/interrupt.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++
++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 <rpurdie@rpsys.net>");
++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 <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+@@ -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 <sjhill@cotw.com");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
+ MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board");
+--- /dev/null
++++ linux-2.4.21/drivers/mtd/nand/toto.c
+@@ -0,0 +1,205 @@
++/*
++ *  drivers/mtd/nand/toto.c
++ *
++ *  Copyright (c) 2003 Texas Instruments
++ *
++ *  Derived from drivers/mtd/autcpu12.c
++ *
++ *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
++ *
++ * 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 <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++#include <asm/arch/toto.h>
++#include <asm/arch-omap1510/hardware.h>
++#include <asm/arch/gpio.h>
++
++/*
++ * 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 <r-woodruff2@ti.com>");
++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 <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/tx4925/tx4925_nand.h>
++
++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; i<len; i++)
++              tx4925_write_nfmc(buf[i], this->IO_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; i<len; i++)
++              buf[i] = tx4925_read_nfmc(this->IO_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; i<len; i++)
++              if (buf[i] != tx4925_read_nfmc(this->IO_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 <ahennessy@mvista.com>");
++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 <linux/config.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/bootinfo.h>
++#include <linux/delay.h>
++#include <asm/tx4938/rbtx4938.h>
++
++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; i<len; i++)
++              tx4938_write_nfmc(buf[i], this->IO_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; i<len; i++)
++              buf[i] = tx4938_read_nfmc(this->IO_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; i<len; i++)
++              if (buf[i] != tx4938_read_nfmc(this->IO_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 <ahennessy@mvista.com>");
++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 <dwmw2@infradead.org>      */
+-/* $Id$ */
++/* $Id$ */
+ /*
+   The contents of this file are distributed under the GNU General
+@@ -23,15 +23,13 @@
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/init.h>
+-#include <linux/blkpg.h>
++#include <linux/hdreg.h>
+-#ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+-#endif
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/mtd/blktrans.h>
+ /* maximum number of loops while examining next block, to have a
+    chance to detect consistency problems (they should never happen
+@@ -39,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 <linux/blk.h>
+-#include <linux/hdreg.h>
+-
+-/* 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<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */
+-#if LINUX_VERSION_CODE < 0x20328
+-      max_nr:         MAX_NFTLS,      /* maximum number of real */
+-      init:           dummy_init,     /* init function */
+-#endif
+-      part:           part_table,     /* hd struct */
+-      sizes:          nftl_sizes,     /* block sizes */
+-};
+-
+-#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
+-
+-struct NFTLrecord *NFTLs[MAX_NFTLS];
+-static void NFTL_setup(struct mtd_info *mtd)
++static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+-      int i;
+       struct NFTLrecord *nftl;
+       unsigned long temp;
+-      int firstfree = -1;
+-
+-      DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
+-      for (i = 0; i < MAX_NFTLS; i++) {
+-              if (!NFTLs[i] && firstfree == -1)
+-                      firstfree = i;
+-              else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
+-                      /* This is a Spare Media Header for an NFTL we've already found */
+-                      DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
++      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<<NFTL_PARTN_BITS, nftl->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<<NFTL_PARTN_BITS) - 1;
+-              while (p-- > 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<<NFTL_PARTN_BITS, nftl->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<<NFTL_PARTN_BITS)) {
+-                      /* there is no such partition */
+-                      printk("nftl: bad minor number: device = %s\n",
+-                             kdevname(req->rq_dev));
+-                      res = 0; /* fail */
+-                      goto repeat;
+-              }
+-              
+-              nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
+-              DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+-              down(&nftl->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 <linux/kernel.h>
+-#include <linux/module.h>
+ #include <asm/errno.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+-#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
+ #define SECTORSIZE 512
+-char nftlmountrev[]="$Revision$";
++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 <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/vmalloc.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -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 ( ; i<nrparts; i++) {
+               parts[i].size = fl->img->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 <dwmw2@cambridge.redhat.com>");
+--- /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 <linux/config.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/sched.h>
++#include <linux/ptrace.h>
++#include <linux/string.h>
++#include <linux/timer.h>
++#include <linux/major.h>
++#include <linux/ioctl.h>
++#include <linux/hdreg.h>
++#include <linux/list.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++
++#if (LINUX_VERSION_CODE >= 0x20100)
++#include <linux/vmalloc.h>
++#endif
++#if (LINUX_VERSION_CODE >= 0x20303)
++#include <linux/blkpg.h>
++#endif
++
++#include <asm/semaphore.h>
++
++#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 <linux/blk.h>
++
++#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; i<MAX_PARTITIONS; i++)
++              if ((atomic_read(&pt_smcpart->minor[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; zone<pt_smcpart->zoneCount; 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; sc<SECTORS_PER_BLOCK; sc++) {
++                                      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]);
++                              }
++
++                      }
++                      
++                      for(sc=0; sc<SECTORS_PER_BLOCK; sc++) {
++                              if(offset > 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; sc<SECTORS_PER_BLOCK; sc++) {
++                              memset(ssfdc_buffer, 0xFF, OOB_SIZE);
++                              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++) 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; b<SECTOR_SIZE; b++) {
++              if(ssfdc_scratch[b + (sc * SECTOR_SIZE)] != 0xFF) return(0);
++      }
++      for(b=0; b<OOB_SIZE; b++) {
++              if((b==6) || (b==7) || (b==11) || (b==12)) continue;   // Block address fields
++              if(ssfdc_buffer[b] != 0xFF) return(0);
++      }
++    return(1);
++}
++static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone) {
++
++      int new = pt_smcpart->last_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; sc<SECTORS_PER_BLOCK; sc++) {
++                              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_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; b<SECTOR_SIZE; b++) {
++                      if(*(junk + (b + (sc * SECTOR_SIZE))) != 0xFF) {
++                              printk(KERN_WARNING "ssfdc_erase : offset 0x%x, sector 0x%x, byte 0x%x, data 0x%02x, expected 0xff\n"
++                                              , offset, sc, b, *(junk + (b + (sc * SECTOR_SIZE))));
++                              goto end;
++                      }
++              }
++              pt_smcpart->mtd->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; b<OOB_SIZE; b++) {
++                      if(*(oob+b) != 0xFF) {
++                              printk(KERN_WARNING "ssfdc_erase : offset 0x%x, byte 0x%x, oob got 0x%02x, expected 0xff\n", 
++                                              offset, b, *(oob+b));
++                              goto end;
++                      }
++              }
++      }
++
++end:
++      
++    kfree(erase);
++      kfree(junk);
++
++    return ret;
++} /* erase_xfer */
++
++
++
++
++
++int init_ssfdc(void)
++{
++      int result, i;
++
++//    unsigned char smc_status;
++//    #define B01159_FIO_PBASE 0x0000000148000000  /* Physical Base address of SMC control chip  */
++
++      printk(KERN_INFO "SSFDC block device translation layer V1.0\n");
++/*
++      pt_ssfdc_smc = ioremap64(B01159_FIO_PBASE, 1024);
++      if(!pt_ssfdc_smc){
++      printk("ssfdc : failed to map SMC control device\n");
++        return(-EFAULT);
++      }
++      
++      smc_status = in_8((void *)&pt_ssfdc_smc->smc_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<MAX_DEVICES; i++) {
++                      if(SMCParts[i].zone)kfree(SMCParts[i].zone);
++      }
++
++
++      unregister_mtd_user(&ssfdc_notifier);
++      unregister_blkdev(ssfdc_major, "ssfdc");
++      blk_cleanup_queue(BLK_DEFAULT_QUEUE(ssfdc_major));
++
++
++
++      blksize_size[SSFDC_MAJOR] = NULL;
++      del_gendisk(&ssfdc_gendisk);
++
++}
++
++module_init(init_ssfdc);
++module_exit(cleanup_ssfdc);
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Simon Haynes <simon@baydel.com>");
++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 <net/irda/wrapper.h>
+ #include <net/irda/irda_device.h>
++#include <asm/io.h>
+ #include <asm/irq.h>
+ #include <asm/dma.h>
+ #include <asm/hardware.h>
+@@ -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 <nico@cam.org>\n";
++      DRV_NAME ": v1.1 Aug 20 2003 by Nicolas Pitre <nico@cam.org>\n";
+ /* Debugging level */
+ #ifndef SMC_DEBUG
+@@ -67,6 +70,7 @@
+ #include <linux/timer.h>
+ #include <linux/errno.h>
+ #include <linux/ioport.h>
++#include <linux/ethtool.h>
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+@@ -78,6 +82,7 @@
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
++#include <asm/uaccess.h>
+ #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 <asm/arch/ramses.h>
+ #define SMC_IOADDR            (RAMSES_ETH_PHYS + 0x300)
+ #define SMC_IRQ                       ETHERNET_IRQ
++      
++#elif CONFIG_ARCH_RAMSES
++#include <asm/arch/ramses.h>
++#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 <hermes@gibson.dropbear.id.au>";
+ MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller");
+ MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
+ #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 <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+-#include <linux/sched.h>
+ #include <linux/ptrace.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/ioport.h>
+-#include <asm/uaccess.h>
+-#include <asm/io.h>
+-#include <asm/system.h>
+ #include <linux/netdevice.h>
+ #include <linux/if_arp.h>
+ #include <linux/etherdevice.h>
+ #include <linux/wireless.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/system.h>
++
+ #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 <hermes@gibson.dropbear.id.au> and others)";
++static char version[] __initdata = "orinoco.c 0.13e (David Gibson <hermes@gibson.dropbear.id.au> 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 <linux/spinlock.h>
+ #include <linux/netdevice.h>
+ #include <linux/wireless.h>
+-#include <linux/tqueue.h>
++#include <linux/version.h>
+ #include "hermes.h"
++/* Workqueue / task queue backwards compatibility stuff */
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
++#include <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#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 <linux/ptrace.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+-#include <linux/timer.h>
+ #include <linux/ioport.h>
+-#include <asm/uaccess.h>
+-#include <asm/io.h>
+-#include <asm/system.h>
+ #include <linux/netdevice.h>
+ #include <linux/if_arp.h>
+ #include <linux/etherdevice.h>
+@@ -38,7 +34,10 @@
+ #include <pcmcia/cistpl.h>
+ #include <pcmcia/cisreg.h>
+ #include <pcmcia/ds.h>
+-#include <pcmcia/bus_ops.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/system.h>
+ #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 <hermes@gibson.dropbear.id.au> and others)";
++static char version[] __initdata = "orinoco_cs.c 0.13e (David Gibson <hermes@gibson.dropbear.id.au> 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 <linux/kernel.h>
++#include <linux/sched.h>
++
++#include <pcmcia/ss.h>
++
++#include <asm/delay.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/arch/pcmcia.h>
++
++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 <linux/sound.h>
+ #include <linux/soundcard.h>
+ #include <linux/ac97_codec.h>
++#include <linux/pm.h>
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
+@@ -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 <pbl@cypress.com>
++ *    3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
++ *
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/smp_lock.h>
++#include <linux/list.h>
++#include <linux/ioport.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <linux/usb.h>
++
++#include "../hcd.h"
++#include "../hub.h"
++#include "sl811.h"
++
++#define DRIVER_VERSION "v0.30"
++#define MODNAME "SL811"
++#define DRIVER_AUTHOR "Yin Aihua <yinah@couragetech.com.cn>, Henry Nestler <hne@ist1.de>"
++#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 <asm/sl811-hw.h>     /* 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 <video/fbcon.h>
+ #include <video/fbcon-mfb.h>
+-#include <video/fbcon-cfb4.h>
+-#include <video/fbcon-cfb8.h>
+ #include <video/fbcon-cfb16.h>
+ #include <video/lcdctrl.h> /* brightness, contrast, etc. control */
+@@ -57,7 +55,7 @@
+ /*
+  * Complain if VAR is out of range.
+  */
+-#define DEBUG_VAR 1
++#define DEBUG_VAR 0
+ #undef ASSABET_PAL_VIDEO
+@@ -66,16 +64,6 @@
+ void (*pxafb_blank_helper)(int blank);
+ EXPORT_SYMBOL(pxafb_blank_helper);
+-/*
+- * IMHO this looks wrong.  In 8BPP, length should be 8.
+- */
+-static struct pxafb_rgb rgb_8 = {
+-      red:    { offset: 0,  length: 4, },
+-      green:  { offset: 0,  length: 4, },
+-      blue:   { offset: 0,  length: 4, },
+-      transp: { offset: 0,  length: 0, },
+-};
+-
+ static struct pxafb_rgb def_rgb_16 = {
+       red:    { offset: 11, length: 5, },
+       green:  { offset: 5,  length: 6, },
+@@ -99,10 +87,29 @@
+       lccr3:          LCD_LCCR3
+ };
++static struct pxafb_mach_info torisan_fb_info __initdata = {
++      pixclock:       30000,
++      bpp:            LCD_BPP,
++      xres:           320,
++      yres:           240,
++      hsync_len:      2,
++      vsync_len:      2,
++      left_margin:    1,
++      upper_margin:   4,
++      right_margin:   139,
++      lower_margin:   4,
++      sync:           FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++      lccr0:          LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM  | LCCR0_OUM | LCCR0_PAS,
++      lccr3:          0x04700007
++};
++
+ static struct pxafb_mach_info * __init
+ pxafb_get_machine_info(struct pxafb_info *fbi)
+ {
+-      return &pxa_fb_info;
++      if (ramses_lcd_type == 2)
++              return &torisan_fb_info;
++      else
++              return &pxa_fb_info;
+ }
+ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);
+@@ -276,13 +283,7 @@
+                * 16 bits works apparemtly fine in passive mode for those,
+                * so don't complain
+                */
+-              if (machine_is_lubbock() ||
+-                  machine_is_pxa_cerf()) {
+-                      ret = 0;
+-              } else
+-                      /* make sure we are in active mode */
+-                      if ((fbi->lccr0 & LCCR0_PAS))
+-                              ret = 0;
++              ret = 0;
+               break;
+ #endif
+       default:
+@@ -671,7 +672,7 @@
+ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi)
+ {
+       struct pxafb_lcd_reg new_regs;
+-//    u_int pcd = get_pcd(var->pixclock);
++      u_int pcd = get_pcd(var->pixclock);
+       u_long flags;
+       DPRINTK("Configuring PXA LCD\n");
+@@ -710,7 +711,6 @@
+                       fbi->fb.fix.id, var->lower_margin);
+ #endif
+-#if defined (CONFIG_PXA_CERF_PDA)
+       new_regs.lccr0 = fbi->lccr0;
+       new_regs.lccr1 =
+               LCCR1_DisWdth(var->xres) +
+@@ -728,47 +728,9 @@
+               |
+               (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
+               (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+-#elif defined (CONFIG_FB_PXA_QVGA)
+-      new_regs.lccr0 = fbi->lccr0;
+-      new_regs.lccr1 =
+-              LCCR1_DisWdth(var->xres) +
+-              LCCR1_HorSnchWdth(var->hsync_len) +
+-              LCCR1_BegLnDel(var->left_margin) +
+-              LCCR1_EndLnDel(var->right_margin);
+-      new_regs.lccr2 =
+-              LCCR2_DisHght(var->yres) +
+-              LCCR2_VrtSnchWdth(var->vsync_len) +
+-              LCCR2_BegFrmDel(var->upper_margin) +
+-              LCCR2_EndFrmDel(var->lower_margin);
+-      new_regs.lccr3 = fbi->lccr3;
+-#else
+-      // FIXME using hardcoded values for now
+-      new_regs.lccr0 = fbi->lccr0;
+-//            |
+-//            LCCR0_LEN | LCCR0_LDM | LCCR0_BAM |
+-//            LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0);
+-
+-      new_regs.lccr1 = 0x3030A7F;
+-//            LCCR1_DisWdth(var->xres) +
+-//            LCCR1_HorSnchWdth(var->hsync_len) +
+-//            LCCR1_BegLnDel(var->left_margin) +
+-//            LCCR1_EndLnDel(var->right_margin);
+-
+-      new_regs.lccr2 = 0x4EF;
+-//            LCCR2_DisHght(var->yres) +
+-//            LCCR2_VrtSnchWdth(var->vsync_len) +
+-//            LCCR2_BegFrmDel(var->upper_margin) +
+-//            LCCR2_EndFrmDel(var->lower_margin);
+-      new_regs.lccr3 = fbi->lccr3;
+-//    |
+-//            (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
+-//            (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL) |
+-//            LCCR3_ACBsCntOff;
+-#endif
+-
+-//    if (pcd)
+-//            new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
++      if (pcd)
++              new_regs.lccr3 = (new_regs.lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
+       DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
+       DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
+@@ -820,25 +782,6 @@
+               fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */
+       }
+-      DPRINTK("fbi->dmadesc_fblow_cpu = 0x%x\n", fbi->dmadesc_fblow_cpu);
+-      DPRINTK("fbi->dmadesc_fbhigh_cpu = 0x%x\n", fbi->dmadesc_fbhigh_cpu);
+-      DPRINTK("fbi->dmadesc_palette_cpu = 0x%x\n", fbi->dmadesc_palette_cpu);
+-      DPRINTK("fbi->dmadesc_fblow_dma = 0x%x\n", fbi->dmadesc_fblow_dma);
+-      DPRINTK("fbi->dmadesc_fbhigh_dma = 0x%x\n", fbi->dmadesc_fbhigh_dma);
+-      DPRINTK("fbi->dmadesc_palette_dma = 0x%x\n", fbi->dmadesc_palette_dma);
+-
+-      DPRINTK("fbi->dmadesc_fblow_cpu->fdadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fdadr);
+-      DPRINTK("fbi->dmadesc_fbhigh_cpu->fdadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fdadr);
+-      DPRINTK("fbi->dmadesc_palette_cpu->fdadr = 0x%x\n", fbi->dmadesc_palette_cpu->fdadr);
+-
+-      DPRINTK("fbi->dmadesc_fblow_cpu->fsadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fsadr);
+-      DPRINTK("fbi->dmadesc_fbhigh_cpu->fsadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fsadr);
+-      DPRINTK("fbi->dmadesc_palette_cpu->fsadr = 0x%x\n", fbi->dmadesc_palette_cpu->fsadr);
+-
+-      DPRINTK("fbi->dmadesc_fblow_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fblow_cpu->ldcmd);
+-      DPRINTK("fbi->dmadesc_fbhigh_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fbhigh_cpu->ldcmd);
+-      DPRINTK("fbi->dmadesc_palette_cpu->ldcmd = 0x%x\n", fbi->dmadesc_palette_cpu->ldcmd);
+-      
+       fbi->reg_lccr0 = new_regs.lccr0;
+       fbi->reg_lccr1 = new_regs.lccr1;
+       fbi->reg_lccr2 = new_regs.lccr2;
+@@ -873,15 +816,11 @@
+ {
+       DPRINTK("backlight on\n");
+-#ifdef CONFIG_ARCH_PXA_IDP
+-      if(machine_is_pxa_idp()) {      
+-              FB_BACKLIGHT_ON();
+-      }
+-#endif
++      ramses_lcd_backlight_on();
+ }
+ /*
+- * FIXME: move LCD power stuf into pxafb_power_down_lcd()
++ * FIXME: move LCD power stuff into pxafb_power_down_lcd()
+  * Also, I'm expecting that the backlight stuff should
+  * be handled differently.
+  */
+@@ -889,12 +828,7 @@
+ {
+       DPRINTK("backlight off\n");
+-#ifdef CONFIG_ARCH_PXA_IDP
+-      if(machine_is_pxa_idp()) {
+-              FB_BACKLIGHT_OFF();
+-      }
+-#endif
+-      
++      ramses_lcd_backlight_off();
+ }
+ static void pxafb_power_up_lcd(struct pxafb_info *fbi)
+@@ -902,38 +836,16 @@
+       DPRINTK("LCD power on\n");
+       CKEN |= CKEN16_LCD;
+-      if(machine_is_pxa_cerf()) {
+-              lcdctrl_enable();
+-      }
+-
+-#if CONFIG_ARCH_PXA_IDP
+-      /* set GPIOs, etc */
+-      if(machine_is_pxa_idp()) {
+-              // FIXME need to add proper delays
+-              FB_PWR_ON();
+-              FB_VLCD_ON();   // FIXME this should be after scanning starts
+-      }
+-#endif
++      ramses_lcd_power_on();
+ }
+ static void pxafb_power_down_lcd(struct pxafb_info *fbi)
+ {
+       DPRINTK("LCD power off\n");
+-      CKEN &= ~CKEN16_LCD;
+-
+-      if(machine_is_pxa_cerf()) {
+-              lcdctrl_disable();
+-      }
+-      /* set GPIOs, etc */
+-#if CONFIG_ARCH_PXA_IDP
+-      if(machine_is_pxa_idp()) {
+-              // FIXME need to add proper delays
+-              FB_PWR_OFF();
+-              FB_VLCD_OFF();  // FIXME this should be before scanning stops
+-      }
+-#endif
++      ramses_lcd_power_off();
++      CKEN &= ~CKEN16_LCD;
+ }
+ static void pxafb_setup_gpio(struct pxafb_info *fbi)
+@@ -1082,6 +994,8 @@
+               if (old_state != C_DISABLE) {
+                       fbi->state = state;
++                      ramses_lcd_power_off();
++
+                       pxafb_backlight_off(fbi);
+                       if (old_state != C_DISABLE_CLKCHANGE)
+                               pxafb_disable_controller(fbi);
+@@ -1191,6 +1105,7 @@
+               if (state == 0) {
+                       /* Enter D0. */
++//printk("--> pxafb_pm_callback(%d)\n", req);
+                       set_ctrlr_state(fbi, C_ENABLE);
+               } else {
+                       /* Enter D1-D3.  Disable the LCD controller.  */
+@@ -1300,7 +1215,6 @@
+       fbi->fb.disp            = (struct display *)(fbi + 1);
+       fbi->fb.pseudo_palette  = (void *)(fbi->fb.disp + 1);
+-      fbi->rgb[RGB_8]         = &rgb_8;
+       fbi->rgb[RGB_16]        = &def_rgb_16;
+       inf = pxafb_get_machine_info(fbi);
+@@ -1348,11 +1262,6 @@
+       if (!fbi)
+               goto failed;
+-      if(machine_is_pxa_cerf()) {
+-              // brightness&contrast is handled via lcdctrl.
+-              lcdctrl_init();
+-      }
+-
+       /* Initialize video memory */
+       ret = pxafb_map_video_memory(fbi);
+       if (ret)
+--- linux-2.4.21/drivers/video/pxafb.h~ramses-lcd
++++ linux-2.4.21/drivers/video/pxafb.h
+@@ -235,4 +235,22 @@
+ #define LCD_LCCR0                     (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM  | LCCR0_OUM)
+ #define LCD_LCCR3                     (LCCR3_PCP | LCCR3_PixClkDiv(0x12) | LCCR3_Bpp(PXAFB_BPP_BITS) | LCCR3_Acb(0x18))
++#elif defined CONFIG_ARCH_RAMSES
++#define LCD_PIXCLOCK                  100000
++#define LCD_BPP                               PXAFB_BPP
++#define LCD_XRES                      240
++#define LCD_YRES                      320
++#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH       6
++#define LCD_VERTICAL_SYNC_PULSE_WIDTH 1
++#define LCD_BEGIN_OF_LINE_WAIT_COUNT  21
++#define LCD_BEGIN_FRAME_WAIT_COUNT    7
++#define LCD_END_OF_LINE_WAIT_COUNT    21
++#define LCD_END_OF_FRAME_WAIT_COUNT   1
++#define LCD_SYNC                      (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT)
++#define LCD_LCCR0                     (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM  | LCCR0_OUM)
++#define LCD_LCCR3                     (LCCR3_PCP | LCCR3_Bpp(PXAFB_BPP_BITS) | LCCR3_Acb(0xe))
++
++// PCD 21 ist noch ok
++// PIXCLOCK 150000 ergibt LCCR3_PCD von 15
++
+ #endif
+--- linux-2.4.21/fs/Config.in~mtd-cvs
++++ linux-2.4.21/fs/Config.in
+@@ -45,6 +45,18 @@
+ dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
+ if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
+    int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
++   bool 'JFFS2 write-buffering support' CONFIG_JFFS2_FS_WRITEBUFFER
++   bool 'JFFS2 ZLIB compression support (recommended)' CONFIG_JFFS2_ZLIB
++   bool 'JFFS2 RTIME compression support (recommended)' CONFIG_JFFS2_RTIME
++   bool 'JFFS2 RUBIN compression support' CONFIG_JFFS2_RUBIN
++   bool 'JFFS2 LZO compression support' CONFIG_JFFS2_LZO
++   bool 'JFFS2 LZARI compression support' CONFIG_JFFS2_LZARI
++   choice 'JFFS2 default compression mode' \
++        "none                                   CONFIG_JFFS2_CMODE_NONE \
++         priority                               CONFIG_JFFS2_CMODE_PRIORITY \
++         size                                   CONFIG_JFFS2_CMODE_SIZE" priority
++
++   bool 'JFFS2 proc interface support' CONFIG_JFFS2_PROC
+ fi
+ tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+ dep_mbool '  Use linear addressing for cramfs' CONFIG_CRAMFS_LINEAR $CONFIG_CRAMFS
+--- linux-2.4.21/fs/inode.c~mtd-cvs
++++ linux-2.4.21/fs/inode.c
+@@ -942,6 +942,38 @@
+       
+ }
++/**
++ *      ilookup - search for an inode in the inode cache
++ *      @sb:         super block of file system to search
++ *      @ino:        inode number to search for
++ *
++ *      If the inode is in the cache, the inode is returned with an
++ *      incremented reference count.
++ *
++ *      Otherwise, %NULL is returned.
++ *
++ *      This is almost certainly not the function you are looking for.
++ *      If you think you need to use this, consult an expert first.
++ */
++struct inode *ilookup(struct super_block *sb, unsigned long ino)
++{
++      struct list_head * head = inode_hashtable + hash(sb,ino);
++      struct inode * inode;
++
++      spin_lock(&inode_lock);
++      inode = find_inode(sb, ino, head, NULL, NULL);
++      if (inode) {
++              __iget(inode);
++              spin_unlock(&inode_lock);
++              wait_on_inode(inode);
++              return inode;
++      }
++      spin_unlock(&inode_lock);
++
++      return inode;
++}
++
++
+ struct inode *igrab(struct inode *inode)
+ {
+       spin_lock(&inode_lock);
+--- linux-2.4.21/fs/jffs2/Makefile~mtd-cvs
++++ linux-2.4.21/fs/jffs2/Makefile
+@@ -1,25 +1,42 @@
+ #
+-# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+-#
+-# $Id$
+-#
+-# 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).
++# fs/jffs2/Makefile.24
+ #
+-# Note 2! The CFLAGS definitions are now in the main makefile...
++# $Id$
++ifdef OUT_OF_TREE_BUILD
++include $(mtd)/defconfig
+-COMPR_OBJS    := compr.o compr_rubin.o compr_rtime.o pushpull.o \
+-                      compr_zlib.o
+-JFFS2_OBJS    := crc32.o dir.o file.o ioctl.o nodelist.o malloc.o \
+-      read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \
+-      symlink.o build.o erase.o background.o
++# This must be first in the include path, so it goes in $(CC) rather
++# then $(EXTRA_CFLAGS)
+-O_TARGET := jffs2.o
++CC += -I$(mtd)/../../include
++EXTRA_CFLAGS := -g -Werror
+-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
+-obj-m := $(O_TARGET)
++ifndef CONFIG_MTD
++EXTRA_CFLAGS += -DMTD_OUT_OF_TREE
++endif
++
++ifdef NONAND
++EXTRA_CFLAGS += -DNONAND
++endif
++endif
++
++obj-$(CONFIG_JFFS2_FS) += jffs2.o
++
++JFFS2_OBJS := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
++JFFS2_OBJS += read.o nodemgmt.o readinode.o write.o scan.o gc.o
++JFFS2_OBJS += symlink-v24.o build.o erase.o background.o fs.o writev.o
++
++LINUX_OBJS += super-v24.o crc32.o rbtree.o
++
++WBUF_OBJS-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
++
++COMPR_OBJS-$(CONFIG_JFFS2_RUBIN)  += compr_rubin.o
++COMPR_OBJS-$(CONFIG_JFFS2_RTIME)  += compr_rtime.o
++COMPR_OBJS-$(CONFIG_JFFS2_ZLIB)   += compr_zlib.o
++
++obj-y := $(COMPR_OBJS-y) $(JFFS2_OBJS) $(LINUX_OBJS) $(WBUF_OBJS-y)
++O_TARGET := jffs2.o
+ include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/Makefile.common
+@@ -0,0 +1,17 @@
++#
++# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
++#
++# $Id$
++#
++
++obj-$(CONFIG_JFFS2_FS) += jffs2.o
++
++jffs2-y       := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
++jffs2-y       += read.o nodemgmt.o readinode.o write.o scan.o gc.o
++jffs2-y       += symlink.o build.o erase.o background.o fs.o writev.o
++jffs2-y       += super.o
++
++jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER)  += wbuf.o
++jffs2-$(CONFIG_JFFS2_RUBIN)   += compr_rubin.o
++jffs2-$(CONFIG_JFFS2_RTIME)   += compr_rtime.o
++jffs2-$(CONFIG_JFFS2_ZLIB)    += compr_zlib.o
+--- linux-2.4.21/fs/jffs2/background.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/background.c
+@@ -1,61 +1,32 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+-#define __KERNEL_SYSCALLS__
+-
+ #include <linux/kernel.h>
+-#include <linux/sched.h>
+-#include <linux/unistd.h>
+ #include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
+ #include <linux/completion.h>
++#include <linux/suspend.h>
+ #include "nodelist.h"
+ static int jffs2_garbage_collect_thread(void *);
+-static int thread_should_wake(struct jffs2_sb_info *c);
+ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
+ {
+-      spin_lock_bh(&c->erase_completion_lock);
+-        if (c->gc_task && thread_should_wake(c))
++      spin_lock(&c->erase_completion_lock);
++        if (c->gc_task && jffs2_thread_should_wake(c))
+                 send_sig(SIGHUP, c->gc_task, 1);
+-      spin_unlock_bh(&c->erase_completion_lock);
++      spin_unlock(&c->erase_completion_lock);
+ }
+ /* This must only ever be called when no GC thread is currently running */
+@@ -86,12 +57,12 @@
+ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+ {
+-      spin_lock_bh(&c->erase_completion_lock);
++      spin_lock(&c->erase_completion_lock);
+       if (c->gc_task) {
+               D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+               send_sig(SIGKILL, c->gc_task, 1);
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
++      spin_unlock(&c->erase_completion_lock);
+       wait_for_completion(&c->gc_thread_exit);
+ }
+@@ -99,34 +70,37 @@
+ {
+       struct jffs2_sb_info *c = _c;
+-      daemonize();
+-      current->tty = NULL;
++      daemonize("jffs2_gcd_mtd%d", c->mtd->index);
++      allow_signal(SIGKILL);
++      allow_signal(SIGSTOP);
++      allow_signal(SIGCONT);
++
+       c->gc_task = current;
+       up(&c->gc_thread_start);
+-        sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
+-
+-      /* FIXME in the 2.2 backport */
+-      current->nice = 10;
++      set_user_nice(current, 10);
+       for (;;) {
+-              spin_lock_irq(&current->sigmask_lock);
+-              siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+-              recalc_sigpending(current);
+-              spin_unlock_irq(&current->sigmask_lock);
++              allow_signal(SIGHUP);
+-              if (!thread_should_wake(c)) {
++              if (!jffs2_thread_should_wake(c)) {
+                         set_current_state (TASK_INTERRUPTIBLE);
+                       D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+-                      /* Yes, there's a race here; we checked thread_should_wake() before
+-                         setting current->state to TASK_INTERRUPTIBLE. But it doesn't
++                      /* Yes, there's a race here; we checked jffs2_thread_should_wake()
++                         before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+                          matter - We don't care if we miss a wakeup, because the GC thread
+                          is only an optimisation anyway. */
+                       schedule();
+               }
+                 
+-              if (current->need_resched)
+-                      schedule();
++              if (current->flags & PF_FREEZE) {
++                      refrigerator(0);
++                      /* refrigerator() should recalc sigpending for us
++                         but doesn't. No matter - allow_signal() will. */
++                      continue;
++              }
++
++              cond_resched();
+                 /* Put_super will send a SIGKILL and then wait on the sem. 
+                  */
+@@ -134,9 +108,7 @@
+                         siginfo_t info;
+                         unsigned long signr;
+-                        spin_lock_irq(&current->sigmask_lock);
+-                        signr = dequeue_signal(&current->blocked, &info);
+-                        spin_unlock_irq(&current->sigmask_lock);
++                      signr = dequeue_signal_lock(current, &current->blocked, &info);
+                         switch(signr) {
+                         case SIGSTOP:
+@@ -147,37 +119,27 @@
+                         case SIGKILL:
+                                 D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+-                              spin_lock_bh(&c->erase_completion_lock);
+-                                c->gc_task = NULL;
+-                              spin_unlock_bh(&c->erase_completion_lock);
+-                              complete_and_exit(&c->gc_thread_exit, 0);
++                              goto die;
+                       case SIGHUP:
+                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
+                               break;
+                       default:
+                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+-
+                         }
+                 }
+               /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
+-              spin_lock_irq(&current->sigmask_lock);
+-              siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+-              recalc_sigpending(current);
+-              spin_unlock_irq(&current->sigmask_lock);
++              disallow_signal(SIGHUP);
+               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+-              jffs2_garbage_collect_pass(c);
++              if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
++                      printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
++                      goto die;
+       }
+-}
+-
+-static int thread_should_wake(struct jffs2_sb_info *c)
+-{
+-      D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", 
+-                c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
+-      if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
+-          c->dirty_size > c->sector_size)
+-              return 1;
+-      else 
+-              return 0;
++      }
++ die:
++      spin_lock(&c->erase_completion_lock);
++      c->gc_task = NULL;
++      spin_unlock(&c->erase_completion_lock);
++      complete_and_exit(&c->gc_thread_exit, 0);
+ }
+--- linux-2.4.21/fs/jffs2/build.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/build.c
+@@ -1,47 +1,24 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/mtd/mtd.h>
+ #include "nodelist.h"
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
+ static inline struct jffs2_inode_cache *
+ first_inode_chain(int *i, struct jffs2_sb_info *c)
+@@ -68,38 +45,80 @@
+            ic;                                        \
+            ic = next_inode(&i, ic, (c)))
++
++static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++      struct jffs2_full_dirent *fd;
++
++      D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
++
++      /* For each child, increase nlink */
++      for(fd = ic->scan_dents; fd; fd = fd->next) {
++              struct jffs2_inode_cache *child_ic;
++              if (!fd->ino)
++                      continue;
++
++              /* XXX: Can get high latency here with huge directories */
++
++              child_ic = jffs2_get_ino_cache(c, fd->ino);
++              if (!child_ic) {
++                      printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
++                                fd->name, fd->ino, ic->ino);
++                      jffs2_mark_node_obsolete(c, fd->raw);
++                      continue;
++              }
++
++              if (child_ic->nlink++ && fd->type == DT_DIR) {
++                      printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
++                      if (fd->ino == 1 && ic->ino == 1) {
++                              printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
++                              printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++                      }
++                      /* What do we do about it? */
++              }
++              D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
++              /* Can't free them. We might need them in pass 2 */
++      }
++}
++
+ /* Scan plan:
+  - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+  - Scan directory tree from top down, setting nlink in inocaches
+  - Scan inocaches for inodes with nlink==0
+ */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c)
++static int jffs2_build_filesystem(struct jffs2_sb_info *c)
+ {
+       int ret;
+       int i;
+       struct jffs2_inode_cache *ic;
++      struct jffs2_full_dirent *fd;
++      struct jffs2_full_dirent *dead_fds = NULL;
+       /* First, scan the medium and build all the inode caches with
+          lists of physical nodes */
+-      c->flags |= JFFS2_SB_FLAG_MOUNTING;
++      c->flags |= JFFS2_SB_FLAG_SCANNING;
+       ret = jffs2_scan_medium(c);
+-      c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
+-
++      c->flags &= ~JFFS2_SB_FLAG_SCANNING;
+       if (ret)
+-              return ret;
++              goto exit;
+       D1(printk(KERN_DEBUG "Scanned flash completely\n"));
+-      /* Now build the data map for each inode, marking obsoleted nodes
+-         as such, and also increase nlink of any children. */
++      D2(jffs2_dump_block_lists(c));
++
++      c->flags |= JFFS2_SB_FLAG_BUILDING;
++      /* Now scan the directory tree, increasing nlink according to every dirent found. */
+       for_each_inode(i, c, ic) {
+               D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
+-              ret = jffs2_build_inode_pass1(c, ic);
+-              if (ret) {
+-                      D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
+-                      return ret;
++
++              D1(BUG_ON(ic->ino > c->highest_ino));
++
++              if (ic->scan_dents) {
++                      jffs2_build_inode_pass1(c, ic);
++                      cond_resched();
+               }
+       }
++
+       D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+       /* Next, scan for inodes with nlink == 0 and remove them. If
+@@ -107,181 +126,249 @@
+          children too, and repeat the scan. As that's going to be
+          a fairly uncommon occurrence, it's not so evil to do it this
+          way. Recursion bad. */
+-      do { 
+-              D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
+-              ret = 0;
++      D1(printk(KERN_DEBUG "Pass 2 starting\n"));
++
+               for_each_inode(i, c, ic) {
+                       D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+                       if (ic->nlink)
+                               continue;
+                       
+-                      ret = jffs2_build_remove_unlinked_inode(c, ic);
+-                      if (ret)
+-                              break;
+-              /* -EAGAIN means the inode's nlink was zero, so we deleted it,
+-                 and furthermore that it had children and their nlink has now
+-                 gone to zero too. So we have to restart the scan. */
++              jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++              cond_resched();
++      } 
++
++      D1(printk(KERN_DEBUG "Pass 2a starting\n"));
++
++      while (dead_fds) {
++              fd = dead_fds;
++              dead_fds = fd->next;
++
++              ic = jffs2_get_ino_cache(c, fd->ino);
++              D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
++
++              if (ic)
++                      jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++              jffs2_free_full_dirent(fd);
+               } 
+-      } while(ret == -EAGAIN);
+       
+       D1(printk(KERN_DEBUG "Pass 2 complete\n"));
+       
+-      /* Finally, we can scan again and free the dirent nodes and scan_info structs */
++      /* Finally, we can scan again and free the dirent structs */
+       for_each_inode(i, c, ic) {
+-              struct jffs2_scan_info *scan = ic->scan;
+-              struct jffs2_full_dirent *fd;
+               D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
+-              if (!scan) {
+-                      if (ic->nlink) {
+-                              D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
++
++              while(ic->scan_dents) {
++                      fd = ic->scan_dents;
++                      ic->scan_dents = fd->next;
++                      jffs2_free_full_dirent(fd);
+                       }
+-                      continue;
++              ic->scan_dents = NULL;
++              cond_resched();
+               }
+-              ic->scan = NULL;
+-              while(scan->dents) {
+-                      fd = scan->dents;
+-                      scan->dents = fd->next;
++      c->flags &= ~JFFS2_SB_FLAG_BUILDING;
++      
++      D1(printk(KERN_DEBUG "Pass 3 complete\n"));
++      D2(jffs2_dump_block_lists(c));
++
++      /* Rotate the lists by some number to ensure wear levelling */
++      jffs2_rotate_lists(c);
++
++      ret = 0;
++
++exit:
++      if (ret) {
++              for_each_inode(i, c, ic) {
++                      while(ic->scan_dents) {
++                              fd = ic->scan_dents;
++                              ic->scan_dents = fd->next;
+                       jffs2_free_full_dirent(fd);
+               }
+-              kfree(scan);
+       }
+-      D1(printk(KERN_DEBUG "Pass 3 complete\n"));
++      }
+       return ret;
+ }
+       
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
+ {
+-      struct jffs2_tmp_dnode_info *tn;
++      struct jffs2_raw_node_ref *raw;
+       struct jffs2_full_dirent *fd;
+-      struct jffs2_node_frag *fraglist = NULL;
+-      struct jffs2_tmp_dnode_info *metadata = NULL;
+-
+-      D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
+-      if (ic->ino > c->highest_ino)
+-              c->highest_ino = ic->ino;
+-
+-      if (!ic->scan->tmpnodes && ic->ino != 1) {
+-              D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
+-      }
+-      /* Build the list to make sure any obsolete nodes are marked as such */
+-      while(ic->scan->tmpnodes) {
+-              tn = ic->scan->tmpnodes;
+-              ic->scan->tmpnodes = tn->next;
+               
+-              if (metadata && tn->version > metadata->version) {
+-                      D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
+-                                metadata->fn->raw->flash_offset &~3));
++      D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+                       
+-                      jffs2_free_full_dnode(metadata->fn);
+-                      jffs2_free_tmp_dnode_info(metadata);
+-                      metadata = NULL;
++      raw = ic->nodes;
++      while (raw != (void *)ic) {
++              struct jffs2_raw_node_ref *next = raw->next_in_ino;
++              D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
++              jffs2_mark_node_obsolete(c, raw);
++              raw = next;
+               }
+                       
+-              if (tn->fn->size) {
+-                      jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
+-                      jffs2_free_tmp_dnode_info(tn);
+-              } else {
+-                      if (!metadata) {
+-                              metadata = tn;
+-                      } else {
+-                              D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
+-                                        tn->fn->raw->flash_offset &~3));
+-                              
+-                              jffs2_free_full_dnode(tn->fn);
+-                              jffs2_free_tmp_dnode_info(tn);
+-                      }
+-              }
+-      }
++      if (ic->scan_dents) {
++              int whinged = 0;
++              D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+               
+-      /* OK. Now clear up */
+-      if (metadata) {
+-              jffs2_free_full_dnode(metadata->fn);
+-              jffs2_free_tmp_dnode_info(metadata);
+-      }
+-      metadata = NULL;
++              while(ic->scan_dents) {
++                      struct jffs2_inode_cache *child_ic;
+       
+-      while (fraglist) {
+-              struct jffs2_node_frag *frag;
+-              frag = fraglist;
+-              fraglist = fraglist->next;
++                      fd = ic->scan_dents;
++                      ic->scan_dents = fd->next;
+               
+-              if (frag->node && !(--frag->node->frags)) {
+-                      jffs2_free_full_dnode(frag->node);
++                      if (!fd->ino) {
++                              /* It's a deletion dirent. Ignore it */
++                              D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
++                              jffs2_free_full_dirent(fd);
++                              continue;
+               }
+-              jffs2_free_node_frag(frag);
++                      if (!whinged) {
++                              whinged = 1;
++                              printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+       }
+-      /* Now for each child, increase nlink */
+-      for(fd=ic->scan->dents; fd; fd = fd->next) {
+-              struct jffs2_inode_cache *child_ic;
+-              if (!fd->ino)
+-                      continue;
++                      D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
++                                fd->name, fd->ino));
+               child_ic = jffs2_get_ino_cache(c, fd->ino);
+               if (!child_ic) {
+-                      printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+-                                fd->name, fd->ino, ic->ino);
++                              printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
++                              jffs2_free_full_dirent(fd);
+                       continue;
+               }
+-              if (child_ic->nlink++ && fd->type == DT_DIR) {
+-                      printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+-                      if (fd->ino == 1 && ic->ino == 1) {
+-                              printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+-                              printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++                      /* Reduce nlink of the child. If it's now zero, stick it on the 
++                         dead_fds list to be cleaned up later. Else just free the fd */
++
++                      child_ic->nlink--;
++                      
++                      if (!child_ic->nlink) {
++                              D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
++                                        fd->ino, fd->name));
++                              fd->next = *dead_fds;
++                              *dead_fds = fd;
++                      } else {
++                              D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
++                                        fd->ino, fd->name, child_ic->nlink));
++                              jffs2_free_full_dirent(fd);
+                       }
+-                      /* What do we do about it? */
+               }
+-              D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+-              /* Can't free them. We might need them in pass 2 */
+       }
+-      return 0;
++
++      /*
++         We don't delete the inocache from the hash list and free it yet. 
++         The erase code will do that, when all the nodes are completely gone.
++      */
+ }
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+ {
+-      struct jffs2_raw_node_ref *raw;
+-      struct jffs2_full_dirent *fd;
+-      int ret = 0;
++      uint32_t size;
+-      if(!ic->scan) {
+-              D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
+-              return 0;
+-      }
++      /* Deletion should almost _always_ be allowed. We're fairly
++         buggered once we stop allowing people to delete stuff
++         because there's not enough free space... */
++      c->resv_blocks_deletion = 2;
+-      D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
++      /* Be conservative about how much space we need before we allow writes. 
++         On top of that which is required for deletia, require an extra 2%
++         of the medium to be available, for overhead caused by nodes being
++         split across blocks, etc. */
+       
+-      for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+-              D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+-              jffs2_mark_node_obsolete(c, raw);
+-      }
++      size = c->flash_size / 50; /* 2% of flash size */
++      size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
++      size += c->sector_size - 1; /* ... and round up */
+-      if (ic->scan->dents) {
+-              printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
++      c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
+       
+-              while(ic->scan->dents) {
+-                      struct jffs2_inode_cache *child_ic;
++      /* When do we let the GC thread run in the background */
+-                      fd = ic->scan->dents;
+-                      ic->scan->dents = fd->next;
++      c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
+-                      D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+-                                fd->name, fd->ino));
++      /* When do we allow garbage collection to merge nodes to make 
++         long-term progress at the expense of short-term space exhaustion? */
++      c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
+                       
+-                      child_ic = jffs2_get_ino_cache(c, fd->ino);
+-                      if (!child_ic) {
+-                              printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+-                              continue;
++      /* When do we allow garbage collection to eat from bad blocks rather
++         than actually making progress? */
++      c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
++
++      /* If there's less than this amount of dirty space, don't bother
++         trying to GC to make more space. It'll be a fruitless task */
++      c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
++
++      D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
++                c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
++      D1(printk(KERN_DEBUG "Blocks required to allow deletion:    %d (%d KiB)\n",
++                c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
++      D1(printk(KERN_DEBUG "Blocks required to allow writes:      %d (%d KiB)\n",
++                c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
++      D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
++                c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
++      D1(printk(KERN_DEBUG "Blocks required to allow GC merges:   %d (%d KiB)\n",
++                c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
++      D1(printk(KERN_DEBUG "Blocks required to GC bad blocks:     %d (%d KiB)\n",
++                c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
++      D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
++                c->nospc_dirty_size));
++} 
++
++int jffs2_do_mount_fs(struct jffs2_sb_info *c)
++{
++      int i;
++
++      c->free_size = c->flash_size;
++      c->nr_blocks = c->flash_size / c->sector_size;
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks);
++      else
++              c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
++      if (!c->blocks)
++              return -ENOMEM;
++      for (i=0; i<c->nr_blocks; i++) {
++              INIT_LIST_HEAD(&c->blocks[i].list);
++              c->blocks[i].offset = i * c->sector_size;
++              c->blocks[i].free_size = c->sector_size;
++              c->blocks[i].dirty_size = 0;
++              c->blocks[i].wasted_size = 0;
++              c->blocks[i].unchecked_size = 0;
++              c->blocks[i].used_size = 0;
++              c->blocks[i].first_node = NULL;
++              c->blocks[i].last_node = NULL;
++              c->blocks[i].bad_count = 0;
+                       }
+-                      jffs2_free_full_dirent(fd);
+-                      child_ic->nlink--;
++
++      init_MUTEX(&c->alloc_sem);
++      init_MUTEX(&c->erase_free_sem);
++      init_waitqueue_head(&c->erase_wait);
++      init_waitqueue_head(&c->inocache_wq);
++      spin_lock_init(&c->erase_completion_lock);
++      spin_lock_init(&c->inocache_lock);
++
++      INIT_LIST_HEAD(&c->clean_list);
++      INIT_LIST_HEAD(&c->very_dirty_list);
++      INIT_LIST_HEAD(&c->dirty_list);
++      INIT_LIST_HEAD(&c->erasable_list);
++      INIT_LIST_HEAD(&c->erasing_list);
++      INIT_LIST_HEAD(&c->erase_pending_list);
++      INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
++      INIT_LIST_HEAD(&c->erase_complete_list);
++      INIT_LIST_HEAD(&c->free_list);
++      INIT_LIST_HEAD(&c->bad_list);
++      INIT_LIST_HEAD(&c->bad_used_list);
++      c->highest_ino = 1;
++
++      if (jffs2_build_filesystem(c)) {
++              D1(printk(KERN_DEBUG "build_fs failed\n"));
++              jffs2_free_ino_caches(c);
++              jffs2_free_raw_node_refs(c);
++              if (c->mtd->flags & MTD_NO_VIRTBLOCKS) {
++                      vfree(c->blocks);
++              } else {
++                      kfree(c->blocks);
+               }
+-              ret = -EAGAIN;
++              return -EIO;
+       }
+-      kfree(ic->scan);
+-      ic->scan = NULL;
+-      //      jffs2_del_ino_cache(c, ic);
+-      //      jffs2_free_inode_cache(ic);
+-      return ret;
++
++      jffs2_calc_trigger_levels(c);
++
++      return 0;
+ }
+--- linux-2.4.21/fs/jffs2/compr.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr.c
+@@ -1,151 +1,479 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  * Created by Arjan van de Ven <arjanv@redhat.com>
+  *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
++ *                    University of Szeged, Hungary
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+-#include <linux/kernel.h>
+-#include <linux/string.h>
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/jffs2.h>
++#include "compr.h"
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
++static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
++/* Available compressors are on this list */
++static LIST_HEAD(jffs2_compressor_list);
++
++/* Actual compression mode */
++static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
++
++void jffs2_set_compression_mode(int mode) 
++{
++        jffs2_compression_mode = mode;
++}
++
++int jffs2_get_compression_mode(void)
++{
++        return jffs2_compression_mode;
++}
++
++/* Statistics for blocks stored without compression */
++static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+ /* jffs2_compress:
+  * @data: Pointer to uncompressed data
+- * @cdata: Pointer to buffer for compressed data
++ * @cdata: Pointer to returned pointer to buffer for compressed data
+  * @datalen: On entry, holds the amount of data available for compression.
+  *    On exit, expected to hold the amount of data actually compressed.
+  * @cdatalen: On entry, holds the amount of space available for compressed
+  *    data. On exit, expected to hold the actual size of the compressed
+  *    data.
+  *
+- * Returns: Byte to be stored with data indicating compression type used.
++ * Returns: Lower byte to be stored with data indicating compression type used.
+  * Zero is used to show that the data could not be compressed - the 
+  * compressed version was actually larger than the original.
++ * Upper byte will be used later. (soon)
+  *
+  * If the cdata buffer isn't large enough to hold all the uncompressed data,
+  * jffs2_compress should compress as much as will fit, and should set 
+  * *datalen accordingly to show the amount of data which were compressed.
+  */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                  __u32 *datalen, __u32 *cdatalen)
++uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                           unsigned char *data_in, unsigned char **cpage_out, 
++                           uint32_t *datalen, uint32_t *cdatalen)
+ {
+-      int ret;
++      int ret = JFFS2_COMPR_NONE;
++        int compr_ret;
++        struct jffs2_compressor *this, *best=NULL;
++        unsigned char *output_buf = NULL, *tmp_buf;
++        uint32_t orig_slen, orig_dlen;
++        uint32_t best_slen=0, best_dlen=0;
+-      ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
+-      if (!ret) {
+-              return JFFS2_COMPR_ZLIB;
++        switch (jffs2_compression_mode) {
++        case JFFS2_COMPR_MODE_NONE:
++                break;
++        case JFFS2_COMPR_MODE_PRIORITY:
++                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
++                if (!output_buf) {
++                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
++                        goto out;
+       }
+-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
+-      ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
+-      if (!ret) {
+-              return JFFS2_COMPR_DYNRUBIN;
++                orig_slen = *datalen;
++                orig_dlen = *cdatalen;
++                spin_lock(&jffs2_compressor_list_lock);
++                list_for_each_entry(this, &jffs2_compressor_list, list) {
++                        /* Skip decompress-only backwards-compatibility and disabled modules */
++                        if ((!this->compress)||(this->disabled))
++                                continue;
++
++                        this->usecount++;
++                        spin_unlock(&jffs2_compressor_list_lock);
++                        *datalen  = orig_slen;
++                        *cdatalen = orig_dlen;
++                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
++                        spin_lock(&jffs2_compressor_list_lock);
++                        this->usecount--;
++                        if (!compr_ret) {
++                                ret = this->compr;
++                                this->stat_compr_blocks++;
++                                this->stat_compr_orig_size += *datalen;
++                                this->stat_compr_new_size  += *cdatalen;
++                                break;
+       }
+-#endif
+-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
+-      ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
+-      if (!ret) {
+-              return JFFS2_COMPR_RUBINMIPS;
+       }
+-#endif
+-      /* rtime does manage to recompress already-compressed data */
+-      ret = rtime_compress(data_in, cpage_out, datalen, cdatalen);
+-      if (!ret) {
+-              return JFFS2_COMPR_RTIME;
++                spin_unlock(&jffs2_compressor_list_lock);
++                if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
++                break;
++        case JFFS2_COMPR_MODE_SIZE:
++                orig_slen = *datalen;
++                orig_dlen = *cdatalen;
++                spin_lock(&jffs2_compressor_list_lock);
++                list_for_each_entry(this, &jffs2_compressor_list, list) {
++                        /* Skip decompress-only backwards-compatibility and disabled modules */
++                        if ((!this->compress)||(this->disabled))
++                                continue;
++                        /* Allocating memory for output buffer if necessary */
++                        if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
++                                spin_unlock(&jffs2_compressor_list_lock);
++                                kfree(this->compr_buf);
++                                spin_lock(&jffs2_compressor_list_lock);
++                                this->compr_buf_size=0;
++                                this->compr_buf=NULL;
+       }
+-#if 0
+-      /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
+-      /* If we get here, no compression is going to work */
+-      /* But we might want to use the fragmentation part -- Arjan */
+-      memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
+-      if (*datalen > *cdatalen)
++                        if (!this->compr_buf) {
++                                spin_unlock(&jffs2_compressor_list_lock);
++                                tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
++                                spin_lock(&jffs2_compressor_list_lock);
++                                if (!tmp_buf) {
++                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
++                                        continue;
++                                }
++                                else {
++                                        this->compr_buf = tmp_buf;
++                                        this->compr_buf_size = orig_dlen;
++                                }
++                        }
++                        this->usecount++;
++                        spin_unlock(&jffs2_compressor_list_lock);
++                        *datalen  = orig_slen;
++                        *cdatalen = orig_dlen;
++                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
++                        spin_lock(&jffs2_compressor_list_lock);
++                        this->usecount--;
++                        if (!compr_ret) {
++                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
++                                        best_dlen = *cdatalen;
++                                        best_slen = *datalen;
++                                        best = this;
++                                }
++                        }
++                }
++                if (best_dlen) {
++                        *cdatalen = best_dlen;
++                        *datalen  = best_slen;
++                        output_buf = best->compr_buf;
++                        best->compr_buf = NULL;
++                        best->compr_buf_size = 0;
++                        best->stat_compr_blocks++;
++                        best->stat_compr_orig_size += best_slen;
++                        best->stat_compr_new_size  += best_dlen;
++                        ret = best->compr;
++                }
++                spin_unlock(&jffs2_compressor_list_lock);
++                break;
++        default:
++                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
++        }
++ out:
++        if (ret == JFFS2_COMPR_NONE) {
++              *cpage_out = data_in;
+               *datalen = *cdatalen;
+-#endif                
+-      return JFFS2_COMPR_NONE; /* We failed to compress */
+-
++                none_stat_compr_blocks++;
++                none_stat_compr_size += *datalen;
++        }
++        else {
++                *cpage_out = output_buf;
++        }
++      return ret;
+ }
+-
+-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
+-                   unsigned char *data_out, __u32 cdatalen, __u32 datalen)
++int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                   uint16_t comprtype, unsigned char *cdata_in, 
++                   unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
+ {
+-      switch (comprtype) {
++        struct jffs2_compressor *this;
++        int ret;
++
++      /* Older code had a bug where it would write non-zero 'usercompr'
++         fields. Deal with it. */
++      if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
++              comprtype &= 0xff;
++
++      switch (comprtype & 0xff) {
+       case JFFS2_COMPR_NONE:
+               /* This should be special-cased elsewhere, but we might as well deal with it */
+               memcpy(data_out, cdata_in, datalen);
++                none_stat_decompr_blocks++;
+               break;
+-
+       case JFFS2_COMPR_ZERO:
+               memset(data_out, 0, datalen);
+               break;
++      default:
++                spin_lock(&jffs2_compressor_list_lock);
++                list_for_each_entry(this, &jffs2_compressor_list, list) {
++                        if (comprtype == this->compr) {
++                                this->usecount++;
++                                spin_unlock(&jffs2_compressor_list_lock);
++                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
++                                spin_lock(&jffs2_compressor_list_lock);
++                                if (ret) {
++                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
++                                }
++                                else {
++                                        this->stat_decompr_blocks++;
++                                }
++                                this->usecount--;
++                                spin_unlock(&jffs2_compressor_list_lock);
++                                return ret;
++                        }
++                }
++              printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
++                spin_unlock(&jffs2_compressor_list_lock);
++              return -EIO;
++      }
++      return 0;
++}
+-      case JFFS2_COMPR_ZLIB:
+-              zlib_decompress(cdata_in, data_out, cdatalen, datalen);
+-              break;
++int jffs2_register_compressor(struct jffs2_compressor *comp)
++{
++        struct jffs2_compressor *this;
+-      case JFFS2_COMPR_RTIME:
+-              rtime_decompress(cdata_in, data_out, cdatalen, datalen);
+-              break;
++        if (!comp->name) {
++                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
++                return -1;
++        }
++        comp->compr_buf_size=0;
++        comp->compr_buf=NULL;
++        comp->usecount=0;
++        comp->stat_compr_orig_size=0;
++        comp->stat_compr_new_size=0;
++        comp->stat_compr_blocks=0;
++        comp->stat_decompr_blocks=0;
++        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
++
++        spin_lock(&jffs2_compressor_list_lock);
++
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                if (this->priority < comp->priority) {
++                        list_add(&comp->list, this->list.prev);
++                        goto out;
++                }
++        }
++        list_add_tail(&comp->list, &jffs2_compressor_list);
++out:
++        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
++                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
++        })
++
++        spin_unlock(&jffs2_compressor_list_lock);
++
++        return 0;
++}
++
++int jffs2_unregister_compressor(struct jffs2_compressor *comp)
++{
++        D2(struct jffs2_compressor *this;)
++
++        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
++
++        spin_lock(&jffs2_compressor_list_lock);
++
++        if (comp->usecount) {
++                spin_unlock(&jffs2_compressor_list_lock);
++                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
++                return -1;
++        }
++        list_del(&comp->list);
++
++        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
++                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
++        })
++        spin_unlock(&jffs2_compressor_list_lock);
++        return 0;
++}
++
++#ifdef CONFIG_JFFS2_PROC
++
++#define JFFS2_STAT_BUF_SIZE 16000
++
++char *jffs2_list_compressors(void)
++{
++        struct jffs2_compressor *this;
++        char *buf, *act_buf;
++
++        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
++                if ((this->disabled)||(!this->compress))
++                        act_buf += sprintf(act_buf,"disabled");
++                else
++                        act_buf += sprintf(act_buf,"enabled");
++                act_buf += sprintf(act_buf,"\n");
++        }
++        return buf;
++}
++
++char *jffs2_stats(void)
++{
++        struct jffs2_compressor *this;
++        char *buf, *act_buf;
++
++        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
++
++        act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
++        act_buf += sprintf(act_buf,"%10s   ","none");
++        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
++                           none_stat_compr_size, none_stat_decompr_blocks);
++        spin_lock(&jffs2_compressor_list_lock);
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                act_buf += sprintf(act_buf,"%10s ",this->name);
++                if ((this->disabled)||(!this->compress))
++                        act_buf += sprintf(act_buf,"- ");
++                else
++                        act_buf += sprintf(act_buf,"+ ");
++                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
++                                   this->stat_compr_new_size, this->stat_compr_orig_size, 
++                                   this->stat_decompr_blocks);
++                act_buf += sprintf(act_buf,"\n");
++        }
++        spin_unlock(&jffs2_compressor_list_lock);
++
++        return buf;
++}
++
++char *jffs2_get_compression_mode_name(void) 
++{
++        switch (jffs2_compression_mode) {
++        case JFFS2_COMPR_MODE_NONE:
++                return "none";
++        case JFFS2_COMPR_MODE_PRIORITY:
++                return "priority";
++        case JFFS2_COMPR_MODE_SIZE:
++                return "size";
++        }
++        return "unkown";
++}
++
++int jffs2_set_compression_mode_name(const char *name) 
++{
++        if (!strcmp("none",name)) {
++                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
++                return 0;
++        }
++        if (!strcmp("priority",name)) {
++                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
++                return 0;
++        }
++        if (!strcmp("size",name)) {
++                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
++                return 0;
++        }
++        return 1;
++}
++
++static int jffs2_compressor_Xable(const char *name, int disabled)
++{
++        struct jffs2_compressor *this;
++        spin_lock(&jffs2_compressor_list_lock);
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                if (!strcmp(this->name, name)) {
++                        this->disabled = disabled;
++                        spin_unlock(&jffs2_compressor_list_lock);
++                        return 0;                        
++                }
++        }
++        spin_unlock(&jffs2_compressor_list_lock);
++        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
++        return 1;
++}
++
++int jffs2_enable_compressor_name(const char *name)
++{
++        return jffs2_compressor_Xable(name, 0);
++}
++
++int jffs2_disable_compressor_name(const char *name)
++{
++        return jffs2_compressor_Xable(name, 1);
++}
++
++int jffs2_set_compressor_priority(const char *name, int priority)
++{
++        struct jffs2_compressor *this,*comp;
++        spin_lock(&jffs2_compressor_list_lock);
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                if (!strcmp(this->name, name)) {
++                        this->priority = priority;
++                        comp = this;
++                        goto reinsert;
++                }
++        }
++        spin_unlock(&jffs2_compressor_list_lock);
++        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);        
++        return 1;
++reinsert:
++        /* list is sorted in the order of priority, so if
++           we change it we have to reinsert it into the
++           good place */
++        list_del(&comp->list);
++        list_for_each_entry(this, &jffs2_compressor_list, list) {
++                if (this->priority < comp->priority) {
++                        list_add(&comp->list, this->list.prev);
++                        spin_unlock(&jffs2_compressor_list_lock);
++                        return 0;
++                }
++        }
++        list_add_tail(&comp->list, &jffs2_compressor_list);
++        spin_unlock(&jffs2_compressor_list_lock);
++        return 0;
++}
+-      case JFFS2_COMPR_RUBINMIPS:
+-#if 0 /* Disabled 23/9/1 */
+-              rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+-#else
+-              printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
+ #endif
+-              break;
+-      case JFFS2_COMPR_DYNRUBIN:
+-#if 1 /* Phase this one out */
+-              dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
++
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
++{
++        if (orig != comprbuf)
++                kfree(comprbuf);
++}
++
++int jffs2_compressors_init(void) 
++{
++/* Registering compressors */
++#ifdef CONFIG_JFFS2_ZLIB
++        jffs2_zlib_init();
++#endif
++#ifdef CONFIG_JFFS2_RTIME
++        jffs2_rtime_init();
++#endif
++#ifdef CONFIG_JFFS2_RUBIN
++        jffs2_rubinmips_init();
++        jffs2_dynrubin_init();
++#endif
++#ifdef CONFIG_JFFS2_LZARI
++        jffs2_lzari_init();
++#endif
++#ifdef CONFIG_JFFS2_LZO
++        jffs2_lzo_init();
++#endif
++/* Setting default compression mode */
++#ifdef CONFIG_JFFS2_CMODE_NONE
++        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
++        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
+ #else
+-              printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
++#ifdef CONFIG_JFFS2_CMODE_SIZE
++        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
++        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
++#else
++        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+ #endif
+-              break;
++#endif
++        return 0;
++}
+-      default:
+-              printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
+-              return -EIO;
+-      }
++int jffs2_compressors_exit(void) 
++{
++/* Unregistering compressors */
++#ifdef CONFIG_JFFS2_LZO
++        jffs2_lzo_exit();
++#endif
++#ifdef CONFIG_JFFS2_LZARI
++        jffs2_lzari_exit();
++#endif
++#ifdef CONFIG_JFFS2_RUBIN
++        jffs2_dynrubin_exit();
++        jffs2_rubinmips_exit();
++#endif
++#ifdef CONFIG_JFFS2_RTIME
++        jffs2_rtime_exit();
++#endif
++#ifdef CONFIG_JFFS2_ZLIB
++        jffs2_zlib_exit();
++#endif
+       return 0;
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/compr.h
+@@ -0,0 +1,118 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
++ *                    University of Szeged, Hungary
++ *
++ * For licensing information, see the file 'LICENCE' in the 
++ * jffs2 directory.
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __JFFS2_COMPR_H__
++#define __JFFS2_COMPR_H__
++
++#include <linux/kernel.h>
++#include <linux/vmalloc.h>
++#include <linux/list.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/jffs2.h>
++#include <linux/jffs2_fs_i.h>
++#include <linux/jffs2_fs_sb.h>
++#include "nodelist.h"
++
++#define JFFS2_RUBINMIPS_PRIORITY 10
++#define JFFS2_DYNRUBIN_PRIORITY  20
++#define JFFS2_LZARI_PRIORITY     30
++#define JFFS2_LZO_PRIORITY       40
++#define JFFS2_RTIME_PRIORITY     50
++#define JFFS2_ZLIB_PRIORITY      60
++
++#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
++#define JFFS2_DYNRUBIN_DISABLED  /*        for decompression */
++
++#define JFFS2_COMPR_MODE_NONE       0
++#define JFFS2_COMPR_MODE_PRIORITY   1
++#define JFFS2_COMPR_MODE_SIZE       2
++
++void jffs2_set_compression_mode(int mode);
++int jffs2_get_compression_mode(void);
++
++struct jffs2_compressor {
++        struct list_head list;
++        int priority;              /* used by prirority comr. mode */
++        char *name;
++        char compr;                /* JFFS2_COMPR_XXX */
++        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
++                        uint32_t *srclen, uint32_t *destlen, void *model);
++        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
++                        uint32_t cdatalen, uint32_t datalen, void *model);
++        int usecount;
++        int disabled;              /* if seted the compressor won't compress */
++        unsigned char *compr_buf;  /* used by size compr. mode */
++        uint32_t compr_buf_size;   /* used by size compr. mode */
++        uint32_t stat_compr_orig_size;
++        uint32_t stat_compr_new_size;
++        uint32_t stat_compr_blocks;
++        uint32_t stat_decompr_blocks;
++};
++
++int jffs2_register_compressor(struct jffs2_compressor *comp);
++int jffs2_unregister_compressor(struct jffs2_compressor *comp);
++
++int jffs2_compressors_init(void);
++int jffs2_compressors_exit(void);
++
++uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                             unsigned char *data_in, unsigned char **cpage_out,
++                             uint32_t *datalen, uint32_t *cdatalen);
++
++int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                     uint16_t comprtype, unsigned char *cdata_in,
++                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
++
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
++
++#ifdef CONFIG_JFFS2_PROC
++int jffs2_enable_compressor_name(const char *name);
++int jffs2_disable_compressor_name(const char *name);
++int jffs2_set_compression_mode_name(const char *mode_name);
++char *jffs2_get_compression_mode_name(void);
++int jffs2_set_compressor_priority(const char *mode_name, int priority);
++char *jffs2_list_compressors(void);
++char *jffs2_stats(void);
++#endif
++
++/* Compressor modules */
++/* These functions will be called by jffs2_compressors_init/exit */
++
++#ifdef CONFIG_JFFS2_RUBIN
++int jffs2_rubinmips_init(void);
++void jffs2_rubinmips_exit(void);
++int jffs2_dynrubin_init(void);
++void jffs2_dynrubin_exit(void);
++#endif
++#ifdef CONFIG_JFFS2_RTIME
++int jffs2_rtime_init(void);
++void jffs2_rtime_exit(void);
++#endif
++#ifdef CONFIG_JFFS2_ZLIB
++int jffs2_zlib_init(void);
++void jffs2_zlib_exit(void);
++#endif
++#ifdef CONFIG_JFFS2_LZARI
++int jffs2_lzari_init(void);
++void jffs2_lzari_exit(void);
++#endif
++#ifdef CONFIG_JFFS2_LZO
++int jffs2_lzo_init(void);
++void jffs2_lzo_exit(void);
++#endif
++
++#endif /* __JFFS2_COMPR_H__ */
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/compr_lzari.c
+@@ -0,0 +1,717 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2004 Patrik Kluba,
++ *                    University of Szeged, Hungary
++ *
++ * For licensing information, see the file 'LICENCE' in the
++ * jffs2 directory.
++ *
++ * $Id$
++ *
++ */
++
++/*
++   Lempel-Ziv-Arithmetic coding compression module for jffs2
++   Based on the LZARI source included in LDS (lossless datacompression sources)
++*/
++
++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
++
++/*
++Original copyright follows:
++
++**************************************************************
++      LZARI.C -- A Data Compression Program
++      (tab = 4 spaces)
++**************************************************************
++      4/7/1989 Haruhiko Okumura
++      Use, distribute, and modify this program freely.
++      Please send me your improved versions.
++              PC-VAN          SCIENCE
++              NIFTY-Serve     PAF01022
++              CompuServe      74050,1022
++**************************************************************
++
++LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake.
++All rights reserved. Permission granted for non-commercial use.
++
++*/
++
++/*
++
++      2004-02-18  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
++                              Removed unused variables and fixed no return value
++
++      2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
++                              Initial release
++
++*/
++
++/* lzari.c */
++
++#define N              4096   /* size of ring buffer */
++#define F                60   /* upper limit for match_length */
++#define THRESHOLD     2   /* encode string into position and length
++                                                 if match_length is greater than this */
++#define NIL                   N       /* index for root of binary search trees */
++
++static unsigned char
++              text_buf[N + F - 1];    /* ring buffer of size N,
++                      with extra F-1 bytes to facilitate string comparison */
++static unsigned long          match_position, match_length,  /* of longest match.  These are
++                      set by the InsertNode() procedure. */
++              lson[N + 1], rson[N + 257], dad[N + 1];  /* left & right children &
++                      parents -- These constitute binary search trees. */
++
++static void InitTree(void)  /* Initialize trees */
++{
++      unsigned long  i;
++
++      /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
++         left children of node i.  These nodes need not be initialized.
++         Also, dad[i] is the parent of node i.  These are initialized to
++         NIL (= N), which stands for 'not used.'
++         For i = 0 to 255, rson[N + i + 1] is the root of the tree
++         for strings that begin with character i.  These are initialized
++         to NIL.  Note there are 256 trees. */
++
++      for (i = N + 1; i <= N + 256; i++) rson[i] = NIL;       /* root */
++      for (i = 0; i < N; i++) dad[i] = NIL;   /* node */
++}
++
++static void InsertNode(unsigned long r)
++      /* Inserts string of length F, text_buf[r..r+F-1], into one of the
++         trees (text_buf[r]'th tree) and returns the longest-match position
++         and length via the global variables match_position and match_length.
++         If match_length = F, then removes the old node in favor of the new
++         one, because the old one will be deleted sooner.
++         Note r plays double role, as tree node and position in buffer. */
++{
++      unsigned long i, p, temp;
++      unsigned char *key;
++      signed long cmp;
++
++      cmp = 1;  key = &text_buf[r];  p = N + 1 + key[0];
++      rson[r] = lson[r] = NIL;  match_length = 0;
++      for ( ; ; ) {
++              if (cmp >= 0) {
++                      if (rson[p] != NIL) p = rson[p];
++                      else {  rson[p] = r;  dad[r] = p;  return;  }
++              } else {
++                      if (lson[p] != NIL) p = lson[p];
++                      else {  lson[p] = r;  dad[r] = p;  return;  }
++              }
++              for (i = 1; i < F; i++)
++                      if ((cmp = key[i] - text_buf[p + i]) != 0)  break;
++              if (i > THRESHOLD) {
++                      if (i > match_length) {
++                              match_position = (r - p) & (N - 1);
++                              if ((match_length = i) >= F) break;
++                      } else if (i == match_length) {
++                              if ((temp = (r - p) & (N - 1)) < match_position)
++                                      match_position = temp;
++                      }
++              }
++      }
++      dad[r] = dad[p];  lson[r] = lson[p];  rson[r] = rson[p];
++      dad[lson[p]] = r;  dad[rson[p]] = r;
++      if (rson[dad[p]] == p) rson[dad[p]] = r;
++      else                   lson[dad[p]] = r;
++      dad[p] = NIL;  /* remove p */
++}
++
++static void DeleteNode(unsigned long p)  /* Delete node p from tree */
++{
++      unsigned long  q;
++      
++      if (dad[p] == NIL) return;  /* not in tree */
++      if (rson[p] == NIL) q = lson[p];
++      else if (lson[p] == NIL) q = rson[p];
++      else {
++              q = lson[p];
++              if (rson[q] != NIL) {
++                      do {  q = rson[q];  } while (rson[q] != NIL);
++                      rson[dad[q]] = lson[q];  dad[lson[q]] = dad[q];
++                      lson[q] = lson[p];  dad[lson[p]] = q;
++              }
++              rson[q] = rson[p];  dad[rson[p]] = q;
++      }
++      dad[q] = dad[p];
++      if (rson[dad[p]] == p) rson[dad[p]] = q;
++      else                   lson[dad[p]] = q;
++      dad[p] = NIL;
++}
++
++/********** Arithmetic Compression **********/
++
++/*  If you are not familiar with arithmetic compression, you should read
++              I. E. Witten, R. M. Neal, and J. G. Cleary,
++                      Communications of the ACM, Vol. 30, pp. 520-540 (1987),
++      from which much have been borrowed.  */
++
++#define M   15
++
++/*    Q1 (= 2 to the M) must be sufficiently large, but not so
++      large as the unsigned long 4 * Q1 * (Q1 - 1) overflows.  */
++
++#define Q1  (1UL << M)
++#define Q2  (2 * Q1)
++#define Q3  (3 * Q1)
++#define Q4  (4 * Q1)
++#define MAX_CUM (Q1 - 1)
++
++#define N_CHAR  (256 - THRESHOLD + F)
++      /* character code = 0, 1, ..., N_CHAR - 1 */
++
++static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1];
++static unsigned long
++      sym_freq[N_CHAR + 1],  /* frequency for symbols */
++      sym_cum[N_CHAR + 1],   /* cumulative freq for symbols */
++      position_cum[N + 1];   /* cumulative freq for positions */
++
++static void StartModel(void)  /* Initialize model */
++{
++      unsigned long ch, sym, i;
++      
++      sym_cum[N_CHAR] = 0;
++      for (sym = N_CHAR; sym >= 1; sym--) {
++              ch = sym - 1;
++              char_to_sym[ch] = sym;  sym_to_char[sym] = ch;
++              sym_freq[sym] = 1;
++              sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym];
++      }
++      sym_freq[0] = 0;  /* sentinel (!= sym_freq[1]) */
++      position_cum[N] = 0;
++      for (i = N; i >= 1; i--)
++              position_cum[i - 1] = position_cum[i] + 10000 / (i + 200);
++                      /* empirical distribution function (quite tentative) */
++                      /* Please devise a better mechanism! */
++}
++
++static void UpdateModel(unsigned long sym)
++{
++      unsigned long c, ch_i, ch_sym;
++      unsigned long i;
++      if (sym_cum[0] >= MAX_CUM) {
++              c = 0;
++              for (i = N_CHAR; i > 0; i--) {
++                      sym_cum[i] = c;
++                      c += (sym_freq[i] = (sym_freq[i] + 1) >> 1);
++              }
++              sym_cum[0] = c;
++      }
++      for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ;
++      if (i < sym) {
++              ch_i = sym_to_char[i];    ch_sym = sym_to_char[sym];
++              sym_to_char[i] = ch_sym;  sym_to_char[sym] = ch_i;
++              char_to_sym[ch_i] = sym;  char_to_sym[ch_sym] = i;
++      }
++      sym_freq[i]++;
++      while (--i > 0) sym_cum[i]++;
++      sym_cum[0]++;
++}
++
++static unsigned long BinarySearchSym(unsigned long x)
++      /* 1      if x >= sym_cum[1],
++         N_CHAR if sym_cum[N_CHAR] > x,
++         i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */
++{
++      unsigned long i, j, k;
++      
++      i = 1;  j = N_CHAR;
++      while (i < j) {
++              k = (i + j) / 2;
++              if (sym_cum[k] > x) i = k + 1;  else j = k;
++      }
++      return i;
++}
++
++unsigned long BinarySearchPos(unsigned long x)
++      /* 0 if x >= position_cum[1],
++         N - 1 if position_cum[N] > x,
++         i such that position_cum[i] > x >= position_cum[i + 1] otherwise */
++{
++      unsigned long i, j, k;
++      
++      i = 1;  j = N;
++      while (i < j) {
++              k = (i + j) / 2;
++              if (position_cum[k] > x) i = k + 1;  else j = k;
++      }
++      return i - 1;
++}
++
++/* modified for block compression */
++/* on return, srclen will contain the number of successfully compressed bytes
++   and dstlen will contain completed compressed bytes */
++
++static int Encode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long *srclen,
++                      unsigned long *dstlen)
++{
++      unsigned long c, i, len, r, s, last_match_length, sym, range;
++      unsigned long low = 0;
++      unsigned long high = Q4;
++      unsigned long shifts = 0;  /* counts for magnifying low and high around Q2 */
++      unsigned char *ip, *op;
++      unsigned long written = 0;
++      unsigned long read = 0;
++      unsigned char buffer = 0;
++      unsigned char mask = 128;
++      unsigned char *srcend = srcbuf + *srclen;
++      unsigned char *dstend = dstbuf + *dstlen;
++      ip = srcbuf;
++      op = dstbuf;
++      StartModel();
++      InitTree();  /* initialize trees */
++      s = 0;  r = N - F;
++      for (i = s; i < r; i++) text_buf[i] = ' ';  /* Clear the buffer with
++              any character that will appear often. */
++      for (len = 0; (len < F) && (ip < srcend); len++)
++              text_buf[r + len] = *(ip++);  /* Read F bytes into the last F bytes of
++                      the buffer */
++      read = len;
++      for (i = 1; i <= F; i++) InsertNode(r - i);  /* Insert the F strings,
++              each of which begins with one or more 'space' characters.  Note
++              the order in which these strings are inserted.  This way,
++              degenerate trees will be less likely to occur. */
++      InsertNode(r);  /* Finally, insert the whole string just read.  The
++              global variables match_length and match_position are set. */
++      do {
++              if (match_length > len) match_length = len;  /* match_length
++                      may be spuriously long near the end of text. */
++              if (match_length <= THRESHOLD) {
++                      match_length = 1;  /* Not long enough match.  Send one byte. */
++                      sym = char_to_sym[text_buf[r]];
++                      range = high - low;
++                      high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
++                      low +=       (range * sym_cum[sym    ]) / sym_cum[0];
++                      for ( ; ; ) {
++                              if (high <= Q2) {
++                                      if ((mask >>= 1) == 0) {
++                                              if (op >= dstend) {
++                                                      *dstlen = written;
++                                                      return -1;
++                                              }
++                                              *(op++) = buffer;
++                                              buffer = 0;
++                                              mask = 128;
++                                              written++;
++                                              *srclen = read;
++                                      }
++                                      for ( ; shifts > 0; shifts--) {
++                                              buffer |= mask;
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                      }
++                              } else if (low >= Q2) {
++                                      buffer |= mask;
++                                      if ((mask >>= 1) == 0) {
++                                              if (op >= dstend) {
++                                                      *dstlen = written;
++                                                      return -1;
++                                              }
++                                              *(op++) = buffer;
++                                              buffer = 0;
++                                              mask = 128;
++                                              written++;
++                                              *srclen = read;
++                                      }
++                                      for ( ; shifts > 0; shifts--) {
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                      }
++                                      low -= Q2;
++                                      high -= Q2;
++                              } else if (low >= Q1 && high <= Q3) {
++                                      shifts++;
++                                      low -= Q1;
++                                      high -= Q1;
++                              } else break;
++                              low += low;  high += high;
++                      }
++                      UpdateModel(sym);
++              } else {
++                      sym = char_to_sym[255 - THRESHOLD + match_length];
++                      range = high - low;
++                      high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
++                      low +=       (range * sym_cum[sym    ]) / sym_cum[0];
++                      for ( ; ; ) {
++                              if (high <= Q2) {
++                                      if ((mask >>= 1) == 0) {
++                                              if (op >= dstend) {
++                                                      *dstlen = written;
++                                                      return -1;
++                                              }
++                                              *(op++) = buffer;
++                                              buffer = 0;
++                                              mask = 128;
++                                              written++;
++                                              *srclen = read;
++                                      }
++                                      for ( ; shifts > 0; shifts--) {
++                                              buffer |= mask;
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                      }
++                              } else if (low >= Q2) {
++                                      buffer |= mask;
++                                      if ((mask >>= 1) == 0) {
++                                              if (op >= dstend) {
++                                                      *dstlen = written;
++                                                      return -1;
++                                              }
++                                              *(op++) = buffer;
++                                              buffer = 0;
++                                              mask = 128;
++                                              written++;
++                                              *srclen = read;
++                                      }
++                                      for ( ; shifts > 0; shifts--) {
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                      }
++                                      low -= Q2;
++                                      high -= Q2;
++                              } else if (low >= Q1 && high <= Q3) {
++                                      shifts++;
++                                      low -= Q1;
++                                      high -= Q1;
++                              } else break;
++                              low += low;  high += high;
++                      }
++                      UpdateModel(sym);
++                      range = high - low;
++                      high = low + (range * position_cum[match_position - 1]) / position_cum[0];
++                      low +=       (range * position_cum[match_position    ]) / position_cum[0];
++                      for ( ; ; ) {
++                              if (high <= Q2) {
++                                      if ((mask >>= 1) == 0) {
++                                              if (op >= dstend) {
++                                                      *dstlen = written;
++                                                      return -1;
++                                              }
++                                              *(op++) = buffer;
++                                              buffer = 0;
++                                              mask = 128;
++                                              written++;
++                                              *srclen = read;
++                                      }
++                                      for ( ; shifts > 0; shifts--) {
++                                              buffer |= mask;
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                      }
++                              } else {
++                                      if (low >= Q2) {
++                                              buffer |= mask;
++                                              if ((mask >>= 1) == 0) {
++                                                      if (op >= dstend) {
++                                                              *dstlen = written;
++                                                              return -1;
++                                                      }
++                                                      *(op++) = buffer;
++                                                      buffer = 0;
++                                                      mask = 128;
++                                                      written++;
++                                                      *srclen = read;
++                                              }
++                                              for ( ; shifts > 0; shifts--) {
++                                                      if ((mask >>= 1) == 0) {
++                                                              if (op >= dstend) {
++                                                                      *dstlen = written;
++                                                                      return -1;
++                                                              }
++                                                              *(op++) = buffer;
++                                                              buffer = 0;
++                                                              mask = 128;
++                                                              written++;
++                                                              *srclen = read;
++                                                      }
++                                              }
++                                              low -= Q2;
++                                              high -= Q2;
++                                      } else {
++                                              if ((low >= Q1) && (high <= Q3)) {
++                                                      shifts++;
++                                                      low -= Q1;
++                                                      high -= Q1;
++                                              } else {
++                                                      break;
++                                              }
++                                      }
++                              }
++                              low += low;
++                              high += high;
++                      }
++              }
++              last_match_length = match_length;
++              for (i = 0; (i < last_match_length) && (ip < srcend); i++) {
++                      c = *(ip++);
++                      DeleteNode(s);
++                      text_buf[s] = c;
++                      if (s < F - 1)
++                              text_buf[s + N] = c;
++                      s = (s + 1) & (N - 1);
++                      r = (r + 1) & (N - 1);
++                      InsertNode(r);
++              }
++              read += i;
++              while (i++ < last_match_length) {
++                      DeleteNode(s);
++                      s = (s + 1) & (N - 1);
++                      r = (r + 1) & (N - 1);
++                      if (--len) InsertNode(r);
++              }
++      } while (len > 0);
++      shifts++;
++      if (low < Q1) {
++              if ((mask >>= 1) == 0) {
++                      if (op >= dstend) {
++                              *dstlen = written;
++                              return -1;
++                      }
++                      *(op++) = buffer;
++                      buffer = 0;
++                      mask = 128;
++                      written++;
++                      *srclen = read;
++              }
++              for ( ; shifts > 0; shifts--) {
++                      buffer |= mask;
++                      if ((mask >>= 1) == 0) {
++                              if (op >= dstend) {
++                                      *dstlen = written;
++                                      return -1;
++                              }
++                              *(op++) = buffer;
++                              buffer = 0;
++                              mask = 128;
++                              written++;
++                              *srclen = read;
++                      }
++              }
++      } else {
++              buffer |= mask;
++              if ((mask >>= 1) == 0) {
++                      if (op >= dstend) {
++                              *dstlen = written;
++                              return -1;
++                      }
++                      *(op++) = buffer;
++                      buffer = 0;
++                      mask = 128;
++                      written++;
++                      *srclen = read;
++              }
++              for ( ; shifts > 0; shifts--) {
++                      if ((mask >>= 1) == 0) {
++                              if (op >= dstend) {
++                                      *dstlen = written;
++                                      return -1;
++                              }
++                              *(op++) = buffer;
++                              buffer = 0;
++                              mask = 128;
++                              written++;
++                              *srclen = read;
++                      }
++              }
++      }
++      for (i = 0; i < 7; i++) {
++              if ((mask >>= 1) == 0) {
++                      if (op >= dstend) {
++                              *dstlen = written;
++                              return -1;
++                      }
++                      *(op++) = buffer;
++                      buffer = 0;
++                      mask = 128;
++                      written++;
++                      *srclen = read;
++              }
++      }
++      *dstlen = written;
++      return 0;
++}
++
++static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen,
++                                      unsigned long dstlen)   /* Just the reverse of Encode(). */
++{
++      unsigned long i, r, j, k, c, range, sym;
++      unsigned char *ip, *op;
++      unsigned char *srcend = srcbuf + srclen;
++      unsigned char *dstend = dstbuf + dstlen;
++      unsigned char buffer = 0;
++      unsigned char mask = 0;
++      unsigned long low = 0;
++      unsigned long high = Q4;
++      unsigned long value = 0;
++      ip = srcbuf;
++      op = dstbuf;
++      for (i = 0; i < M + 2; i++) {
++              value *= 2;
++              if ((mask >>= 1) == 0) {
++                      buffer = (ip >= srcend) ? 0 : *(ip++);
++                      mask = 128;
++              }
++              value += ((buffer & mask) != 0);
++      }
++      StartModel();
++      for (i = 0; i < N - F; i++) text_buf[i] = ' ';
++      r = N - F;
++      while (op < dstend) {
++              range = high - low;
++              sym = BinarySearchSym((unsigned long)
++                              (((value - low + 1) * sym_cum[0] - 1) / range));
++              high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
++              low +=       (range * sym_cum[sym    ]) / sym_cum[0];
++              for ( ; ; ) {
++                      if (low >= Q2) {
++                              value -= Q2;  low -= Q2;  high -= Q2;
++                      } else if (low >= Q1 && high <= Q3) {
++                              value -= Q1;  low -= Q1;  high -= Q1;
++                      } else if (high > Q2) break;
++                      low += low;  high += high;
++                      value *= 2;
++                      if ((mask >>= 1) == 0) {
++                              buffer = (ip >= srcend) ? 0 : *(ip++);
++                              mask = 128;
++                      }
++                      value += ((buffer & mask) != 0);
++              }
++              c = sym_to_char[sym];
++              UpdateModel(sym);
++              if (c < 256) {
++                      if (op >= dstend) return -1;
++                      *(op++) = c;
++                      text_buf[r++] = c;
++                      r &= (N - 1);
++              } else {
++                      j = c - 255 + THRESHOLD;
++                      range = high - low;
++                      i = BinarySearchPos((unsigned long)
++                              (((value - low + 1) * position_cum[0] - 1) / range));
++                      high = low + (range * position_cum[i    ]) / position_cum[0];
++                      low +=       (range * position_cum[i + 1]) / position_cum[0];
++                      for ( ; ; ) {
++                              if (low >= Q2) {
++                                      value -= Q2;  low -= Q2;  high -= Q2;
++                              } else if (low >= Q1 && high <= Q3) {
++                                      value -= Q1;  low -= Q1;  high -= Q1;
++                              } else if (high > Q2) break;
++                              low += low;  high += high;
++                              value *= 2;
++                              if ((mask >>= 1) == 0) {
++                                      buffer = (ip >= srcend) ? 0 : *(ip++);
++                                      mask = 128;
++                              }
++                              value += ((buffer & mask) != 0);
++                      }
++                      i = (r - i - 1) & (N - 1);
++                      for (k = 0; k < j; k++) {
++                              c = text_buf[(i + k) & (N - 1)];
++                              if (op >= dstend) return -1;
++                              *(op++) = c;
++                              text_buf[r++] = c;
++                              r &= (N - 1);
++                      }               
++              }
++      }
++      return 0;
++}
++
++/* interface to jffs2 follows */
++
++#include "compr.h"
++#include <linux/jffs2.h>
++
++int jffs2_lzari_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model);
++
++int jffs2_lzari_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model);
++
++struct jffs2_compressor jffs2_lzari_comp = {
++      .priority = JFFS2_LZARI_PRIORITY,
++      .name = "lzari",
++      .compr = JFFS2_COMPR_LZARI,
++      .compress = &jffs2_lzari_compress,
++      .decompress = &jffs2_lzari_decompress,
++#ifdef JFFS2_LZARI_DISABLED
++      .disabled = 1,
++#else
++      .disabled = 0,
++#endif
++};
++
++int jffs2_lzari_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model)
++{
++      return Encode(input, output, (unsigned long *)sourcelen, (unsigned long *)dstlen);
++}
++
++int jffs2_lzari_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model)
++{
++    return Decode(input, output, sourcelen, dstlen);
++}
++
++int jffs2_lzari_init (void)
++{
++    return jffs2_register_compressor(&jffs2_lzari_comp);
++}
++
++void jffs2_lzari_exit (void)
++{
++    jffs2_unregister_compressor (&jffs2_lzari_comp);
++}
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/compr_lzo.c
+@@ -0,0 +1,2329 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2004 Patrik Kluba,
++ *                    University of Szeged, Hungary
++ *
++ * For licensing information, see the file 'LICENCE' in the
++ * jffs2 directory.
++ *
++ * $Id$
++ *
++ */
++
++/*
++   LZO1X-1 (and -999) compression module for jffs2
++   based on the original LZO sources
++*/
++
++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
++
++/*
++   Original copyright notice follows:
++
++   lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
++   lzo_ptr.h -- low-level pointer constructs
++   lzo_swd.ch -- sliding window dictionary
++   lzoconf.h -- configuration for the LZO real-time data compression library
++   lzo_mchw.ch -- matching functions using a window
++   minilzo.c -- mini subset of the LZO real-time data compression library
++   config1x.h -- configuration for the LZO1X algorithm
++   lzo1x.h -- public interface of the LZO1X compression algorithm
++
++   These files are part of the LZO real-time data compression library.
++
++   Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
++   All Rights Reserved.
++
++   The LZO library 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.
++
++   The LZO library 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 the LZO library; see the file COPYING.
++   If not, write to the Free Software Foundation, Inc.,
++   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++
++   Markus F.X.J. Oberhumer
++   <markus@oberhumer.com>
++*/
++
++/*
++
++      2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
++                              Initial release
++                                      -removed all 16 bit code
++                                      -all sensitive data will be on 4 byte boundary
++                                      -removed check parts for library use
++                                      -removed all but LZO1X-* compression
++                                      
++*/
++
++#ifndef __KERNEL__
++  #include <sys/types.h>
++  #include <stddef.h>
++  #include <string.h>
++  #include <limits.h>
++#else
++  #include <linux/kernel.h>
++  #include <linux/types.h>
++  #include <linux/stddef.h>
++  #include <linux/string.h>
++  #define USHRT_MAX     65535
++  /* #define UINT_MAX      4294967295U */
++#endif
++
++/* data type definitions */
++#define U32 unsigned long
++#define S32 signed long
++#define I32 long
++#define U16 unsigned short
++#define S16 signed short
++#define I16 short
++#define U8 unsigned char
++#define S8 signed char
++#define I8 char
++
++/*************************************/
++
++/* lzo_swd.ch */
++
++#define SWD_N                         N
++#define SWD_F                         F
++#define SWD_THRESHOLD         THRESHOLD
++
++/* shortest unsigned int that 2 * SWD_F + SWD_N (currently 53248) fits in */
++typedef unsigned short swd_uint;
++/* upper limit of that data type */
++#define SWD_UINT_MAX  USHRT_MAX
++
++/* minilzo.c */
++
++#define LZO_VERSION_DATE      "Jul 12 2002"
++#define LZO_VERSION_STRING    "1.08"
++#define LZO_VERSION                   0x1080
++
++/* lzo_ptr.h */
++
++/* Integral types that have *exactly* the same number of bits as a lzo_voidp */
++typedef unsigned long lzo_ptr_t;
++typedef long lzo_sptr_t;
++
++
++/*************************************/
++
++/* config1x.h */
++
++#define M1_MAX_OFFSET 0x0400
++#define M2_MAX_OFFSET 0x0800
++#define M3_MAX_OFFSET 0x4000
++#define M4_MAX_OFFSET 0xbfff
++
++#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET)
++
++#define M1_MIN_LEN            2
++#define M1_MAX_LEN            2
++#define M2_MIN_LEN            3
++#define M2_MAX_LEN            8
++#define M3_MIN_LEN            3
++#define M3_MAX_LEN            33
++#define M4_MIN_LEN            3
++#define M4_MAX_LEN            9
++
++#define M1_MARKER             0
++#define M2_MARKER             64
++#define M3_MARKER             32
++#define M4_MARKER             16
++
++#define MIN_LOOKAHEAD         (M2_MAX_LEN + 1)
++
++/* minilzo.c */
++
++#define LZO_BYTE(x)       ((unsigned char) ((x) & 0xff))
++
++#define LZO_MAX(a,b)        ((a) >= (b) ? (a) : (b))
++#define LZO_MIN(a,b)        ((a) <= (b) ? (a) : (b))
++#define LZO_MAX3(a,b,c)     ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
++#define LZO_MIN3(a,b,c)     ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c))
++
++#define lzo_sizeof(type)    ((lzo_uint) (sizeof(type)))
++
++#define LZO_HIGH(array)     ((lzo_uint) (sizeof(array)/sizeof(*(array))))
++
++#define LZO_SIZE(bits)      (1u << (bits))
++#define LZO_MASK(bits)      (LZO_SIZE(bits) - 1)
++
++#define LZO_LSIZE(bits)     (1ul << (bits))
++#define LZO_LMASK(bits)     (LZO_LSIZE(bits) - 1)
++
++#define LZO_USIZE(bits)     ((lzo_uint) 1 << (bits))
++#define LZO_UMASK(bits)     (LZO_USIZE(bits) - 1)
++
++#define LZO_STYPE_MAX(b)    (((1l  << (8*(b)-2)) - 1l)  + (1l  << (8*(b)-2)))
++#define LZO_UTYPE_MAX(b)    (((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1)))
++
++#define _LZO_STRINGIZE(x)           #x
++#define _LZO_MEXPAND(x)             _LZO_STRINGIZE(x)
++
++#define _LZO_CONCAT2(a,b)           a ## b
++#define _LZO_CONCAT3(a,b,c)         a ## b ## c
++#define _LZO_CONCAT4(a,b,c,d)       a ## b ## c ## d
++#define _LZO_CONCAT5(a,b,c,d,e)     a ## b ## c ## d ## e
++
++#define _LZO_ECONCAT2(a,b)          _LZO_CONCAT2(a,b)
++#define _LZO_ECONCAT3(a,b,c)        _LZO_CONCAT3(a,b,c)
++#define _LZO_ECONCAT4(a,b,c,d)      _LZO_CONCAT4(a,b,c,d)
++#define _LZO_ECONCAT5(a,b,c,d,e)    _LZO_CONCAT5(a,b,c,d,e)
++
++#define lzo_dict_t    const lzo_bytep
++#define lzo_dict_p    lzo_dict_t *
++#define lzo_moff_t    lzo_uint
++
++#define MEMCPY8_DS(dest,src,len) \
++    memcpy(dest,src,len); \
++    dest += len; \
++    src += len
++
++#define MEMCPY_DS(dest,src,len) \
++    do *dest++ = *src++; \
++    while (--len > 0)
++
++#define MEMMOVE_DS(dest,src,len) \
++    do *dest++ = *src++; \
++    while (--len > 0)
++
++#define BZERO8_PTR(s,l,n)   memset((s),0,(lzo_uint)(l)*(n))
++
++#define LZO_BASE 65521u
++#define LZO_NMAX 5552
++
++#define LZO_DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
++#define LZO_DO2(buf,i)  LZO_DO1(buf,i); LZO_DO1(buf,i+1);
++#define LZO_DO4(buf,i)  LZO_DO2(buf,i); LZO_DO2(buf,i+2);
++#define LZO_DO8(buf,i)  LZO_DO4(buf,i); LZO_DO4(buf,i+4);
++#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8);
++
++#define IS_SIGNED(type)       (((type) (-1)) < ((type) 0))
++#define IS_UNSIGNED(type)     (((type) (-1)) > ((type) 0))
++
++#define IS_POWER_OF_2(x)        (((x) & ((x) - 1)) == 0)
++
++#define D_BITS          14
++#define D_INDEX1(d,p)       d = DM((0x21*DX3(p,5,5,6)) >> 5)
++#define D_INDEX2(d,p)       d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
++
++#define LZO_HASH            LZO_HASH_LZO_INCREMENTAL_B
++
++#define DL_MIN_LEN          M2_MIN_LEN
++
++#define D_SIZE        LZO_SIZE(D_BITS)
++#define D_MASK        LZO_MASK(D_BITS)
++
++#define D_HIGH        ((D_MASK >> 1) + 1)
++
++#define DINDEX1             D_INDEX1
++#define DINDEX2             D_INDEX2
++
++#define DX2(p,s1,s2) \
++      (((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
++
++#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
++#define DMS(v,s)        ((lzo_uint) (((v) & (D_MASK >> (s))) << (s)))
++#define DM(v)           DMS(v,0)
++
++#define DENTRY(p,in)                          (p)
++#define GINDEX(m_pos,m_off,dict,dindex,in)    m_pos = dict[dindex]
++
++#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
++      (m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset)
++
++#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
++    (BOUNDS_CHECKING_OFF_IN_EXPR( \
++      (PTR_LT(m_pos,in) || \
++       (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \
++        m_off > max_offset) ))
++
++#define BOUNDS_CHECKING_OFF_IN_EXPR(expr)     (expr)
++
++#define DD_BITS                       0
++#define DD_SIZE         LZO_SIZE(DD_BITS)
++#define DD_MASK         LZO_MASK(DD_BITS)
++
++#define DL_BITS        (D_BITS - DD_BITS)
++#define DL_SIZE        LZO_SIZE(DL_BITS)
++#define DL_MASK        LZO_MASK(DL_BITS)
++
++#define UPDATE_D(dict,drun,dv,p,in)       dict[ DINDEX(dv,p) ] = DENTRY(p,in)
++#define UPDATE_I(dict,drun,index,p,in)    dict[index] = DENTRY(p,in)
++#define UPDATE_P(ptr,drun,p,in)           (ptr)[0] = DENTRY(p,in)
++
++#define __COPY4(dst,src)  * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
++#define COPY4(dst,src)        __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
++
++#define TEST_IP         (ip < ip_end)
++#define TEST_OP         (op <= op_end)
++
++#define NEED_IP(x) \
++            if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
++#define NEED_OP(x) \
++            if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
++#define TEST_LOOKBEHIND(m_pos,out)    if (m_pos < out) goto lookbehind_overrun
++
++/* lzo1x_9x.c */
++
++#define LZO_UINT_MAX  UINT_MAX
++#define N                     M4_MAX_OFFSET
++#define THRESHOLD         1
++#define F                  2048
++
++#define SWD_BEST_OFF  (LZO_MAX3( M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN ) + 1)
++
++/* ../include/lzoconf.h */
++
++typedef U32 lzo_uint32;
++typedef I32 lzo_int32;
++typedef U32 lzo_uint;
++typedef I32 lzo_int;
++typedef int lzo_bool;
++
++#define lzo_byte                U8
++#define lzo_bytep               U8 *
++#define lzo_charp               char *
++#define lzo_voidp               void *
++#define lzo_shortp              short *
++#define lzo_ushortp             unsigned short *
++#define lzo_uint32p             lzo_uint32 *
++#define lzo_int32p              lzo_int32 *
++#define lzo_uintp               lzo_uint *
++#define lzo_intp                lzo_int *
++#define lzo_voidpp              lzo_voidp *
++#define lzo_bytepp              lzo_bytep *
++#define lzo_sizeof_dict_t             sizeof(lzo_bytep)
++
++#define LZO_E_OK                    0
++#define LZO_E_ERROR                 (-1)
++#define LZO_E_OUT_OF_MEMORY         (-2)      /* not used right now */
++#define LZO_E_NOT_COMPRESSIBLE      (-3)      /* not used right now */
++#define LZO_E_INPUT_OVERRUN         (-4)
++#define LZO_E_OUTPUT_OVERRUN        (-5)
++#define LZO_E_LOOKBEHIND_OVERRUN    (-6)
++#define LZO_E_EOF_NOT_FOUND         (-7)
++#define LZO_E_INPUT_NOT_CONSUMED    (-8)
++
++#define LZO_PTR_ALIGN_UP(_ptr,_size) \
++   ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size)))
++#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size)
++
++typedef int
++      (*lzo_compress_t) (const lzo_byte * src, lzo_uint src_len,
++                         lzo_byte * dst, lzo_uintp dst_len,
++                         lzo_voidp wrkmem);
++
++typedef int
++      (*lzo_decompress_t) (const lzo_byte * src, lzo_uint src_len,
++                           lzo_byte * dst, lzo_uintp dst_len,
++                           lzo_voidp wrkmem);
++
++typedef int
++      (*lzo_optimize_t) (lzo_byte * src, lzo_uint src_len,
++                         lzo_byte * dst, lzo_uintp dst_len,
++                         lzo_voidp wrkmem);
++
++typedef int
++      (*lzo_compress_dict_t) (const lzo_byte * src, lzo_uint src_len,
++                              lzo_byte * dst, lzo_uintp dst_len,
++                              lzo_voidp wrkmem,
++                              const lzo_byte * dict, lzo_uint dict_len);
++
++typedef int
++      (*lzo_decompress_dict_t) (const lzo_byte * src, lzo_uint src_len,
++                                lzo_byte * dst, lzo_uintp dst_len,
++                                lzo_voidp wrkmem,
++                                const lzo_byte * dict, lzo_uint dict_len);
++
++typedef int
++      (*lzo_compress_asm_t) (const lzo_byte * src, lzo_uint src_len,
++                             lzo_byte * dst, lzo_uintp dst_len,
++                             lzo_voidp wrkmem);
++
++typedef int
++      (*lzo_decompress_asm_t) (const lzo_byte * src, lzo_uint src_len,
++                               lzo_byte * dst, lzo_uintp dst_len,
++                               lzo_voidp wrkmem);
++
++typedef void (*lzo_progress_callback_t) (lzo_uint, lzo_uint);
++
++typedef union
++{
++      lzo_bytep p;
++      lzo_uint u;
++} __lzo_pu_u;
++typedef union
++{
++      lzo_bytep p;
++      lzo_uint32 u32;
++} __lzo_pu32_u;
++typedef union
++{
++      void *vp;
++      lzo_bytep bp;
++      lzo_uint32 u32;
++      long l;
++} lzo_align_t;
++
++/* lzo1x.h */
++
++#define LZO1X_1_MEM_COMPRESS    ((lzo_uint32) (16384L * lzo_sizeof_dict_t))
++#define LZO1X_999_MEM_COMPRESS  ((lzo_uint32) (14 * 16384L * sizeof(short)))
++
++/* lzo_ptr.h */
++
++#define PTR(a)                                ((lzo_ptr_t) (a))
++#define PTR_LINEAR(a)         PTR(a)
++#define PTR_ALIGNED_4(a)      ((PTR_LINEAR(a) & 3) == 0)
++#define PTR_ALIGNED_8(a)      ((PTR_LINEAR(a) & 7) == 0)
++#define PTR_ALIGNED2_4(a,b)   (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
++#define PTR_ALIGNED2_8(a,b)   (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
++#define PTR_LT(a,b)                   (PTR(a) < PTR(b))
++#define PTR_GE(a,b)                   (PTR(a) >= PTR(b))
++#define PTR_DIFF(a,b)         ((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
++#define pd(a,b)                       ((lzo_uint) ((a)-(b)))
++
++typedef ptrdiff_t lzo_ptrdiff_t;
++
++typedef union
++{
++      char a_char;
++      unsigned char a_uchar;
++      short a_short;
++      unsigned short a_ushort;
++      int a_int;
++      unsigned int a_uint;
++      long a_long;
++      unsigned long a_ulong;
++      lzo_int a_lzo_int;
++      lzo_uint a_lzo_uint;
++      lzo_int32 a_lzo_int32;
++      lzo_uint32 a_lzo_uint32;
++      ptrdiff_t a_ptrdiff_t;
++      lzo_ptrdiff_t a_lzo_ptrdiff_t;
++      lzo_ptr_t a_lzo_ptr_t;
++      lzo_voidp a_lzo_voidp;
++      void *a_void_p;
++      lzo_bytep a_lzo_bytep;
++      lzo_bytepp a_lzo_bytepp;
++      lzo_uintp a_lzo_uintp;
++      lzo_uint *a_lzo_uint_p;
++      lzo_uint32p a_lzo_uint32p;
++      lzo_uint32 *a_lzo_uint32_p;
++      unsigned char *a_uchar_p;
++      char *a_char_p;
++}
++lzo_full_align_t;
++
++/* lzo_mchw.ch */
++
++typedef struct
++{
++      int init;
++
++      lzo_uint look;
++
++      lzo_uint m_len;
++      lzo_uint m_off;
++
++      lzo_uint last_m_len;
++      lzo_uint last_m_off;
++
++      const lzo_byte *bp;
++      const lzo_byte *ip;
++      const lzo_byte *in;
++      const lzo_byte *in_end;
++      lzo_byte *out;
++
++      lzo_progress_callback_t cb;
++
++      lzo_uint textsize;
++      lzo_uint codesize;
++      lzo_uint printcount;
++
++      unsigned long lit_bytes;
++      unsigned long match_bytes;
++      unsigned long rep_bytes;
++      unsigned long lazy;
++
++      lzo_uint r1_lit;
++      lzo_uint r1_m_len;
++
++      unsigned long m1a_m, m1b_m, m2_m, m3_m, m4_m;
++      unsigned long lit1_r, lit2_r, lit3_r;
++}
++lzo1x_999_t;
++
++#define getbyte(c)    ((c).ip < (c).in_end ? *((c).ip)++ : (-1))
++
++/* lzo_swd.ch */
++
++#define SWD_UINT(x)                   ((swd_uint)(x))
++#define SWD_HSIZE                     16384
++#define SWD_MAX_CHAIN         2048
++#define HEAD3(b,p) \
++      (((0x9f5f*(((((lzo_uint32)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1))
++#define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8))
++#define NIL2                          SWD_UINT_MAX
++
++typedef struct
++{
++      lzo_uint n;
++      lzo_uint f;
++      lzo_uint threshold;
++
++      lzo_uint max_chain;
++      lzo_uint nice_length;
++      lzo_bool use_best_off;
++      lzo_uint lazy_insert;
++
++      lzo_uint m_len;
++      lzo_uint m_off;
++      lzo_uint look;
++      int b_char;
++
++      lzo_uint best_off[SWD_BEST_OFF];
++
++      lzo1x_999_t *c;
++      lzo_uint m_pos;
++
++      lzo_uint best_pos[SWD_BEST_OFF];
++
++      const lzo_byte *dict;
++      const lzo_byte *dict_end;
++      lzo_uint dict_len;
++
++      lzo_uint ip;
++      lzo_uint bp;
++      lzo_uint rp;
++      lzo_uint b_size;
++
++      unsigned char *b_wrap;
++
++      lzo_uint node_count;
++      lzo_uint first_rp;
++
++      unsigned char b[SWD_N + SWD_F + SWD_F];
++      swd_uint head3[SWD_HSIZE];
++      swd_uint succ3[SWD_N + SWD_F];
++      swd_uint best3[SWD_N + SWD_F];
++      swd_uint llen3[SWD_HSIZE];
++
++      swd_uint head2[65536L];
++}
++lzo1x_999_swd_t;
++
++#define s_head3(s,key)                s->head3[key]
++#define swd_pos2off(s,pos) \
++      (s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp))
++
++static __inline__ void
++swd_getbyte (lzo1x_999_swd_t * s)
++{
++      int c;
++
++      if ((c = getbyte (*(s->c))) < 0)
++      {
++              if (s->look > 0)
++                      --s->look;
++      }
++      else
++      {
++              s->b[s->ip] = LZO_BYTE (c);
++              if (s->ip < s->f)
++                      s->b_wrap[s->ip] = LZO_BYTE (c);
++      }
++      if (++s->ip == s->b_size)
++              s->ip = 0;
++      if (++s->bp == s->b_size)
++              s->bp = 0;
++      if (++s->rp == s->b_size)
++              s->rp = 0;
++}
++
++static void
++swd_initdict (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
++{
++      s->dict = s->dict_end = NULL;
++      s->dict_len = 0;
++
++      if (!dict || dict_len <= 0)
++              return;
++      if (dict_len > s->n)
++      {
++              dict += dict_len - s->n;
++              dict_len = s->n;
++      }
++
++      s->dict = dict;
++      s->dict_len = dict_len;
++      s->dict_end = dict + dict_len;
++      memcpy (s->b, dict, dict_len);
++      s->ip = dict_len;
++}
++
++static void
++swd_insertdict (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint len)
++{
++      lzo_uint key;
++
++      s->node_count = s->n - len;
++      s->first_rp = node;
++
++      while (len-- > 0)
++      {
++              key = HEAD3 (s->b, node);
++              s->succ3[node] = s_head3 (s, key);
++              s->head3[key] = SWD_UINT (node);
++              s->best3[node] = SWD_UINT (s->f + 1);
++              s->llen3[key]++;
++
++              key = HEAD2 (s->b, node);
++              s->head2[key] = SWD_UINT (node);
++
++              node++;
++      }
++}
++
++static int
++swd_init (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
++{
++
++      s->n = SWD_N;
++      s->f = SWD_F;
++      s->threshold = SWD_THRESHOLD;
++
++
++
++      s->max_chain = SWD_MAX_CHAIN;
++      s->nice_length = SWD_F;
++      s->use_best_off = 0;
++      s->lazy_insert = 0;
++
++      s->b_size = s->n + s->f;
++      if (2 * s->f >= s->n || s->b_size + s->f >= NIL2)
++              return LZO_E_ERROR;
++      s->b_wrap = s->b + s->b_size;
++      s->node_count = s->n;
++
++      memset (s->llen3, 0, sizeof (s->llen3[0]) * SWD_HSIZE);
++      memset (s->head2, 0xff, sizeof (s->head2[0]) * 65536L);
++
++      s->ip = 0;
++      swd_initdict (s, dict, dict_len);
++      s->bp = s->ip;
++      s->first_rp = s->ip;
++
++      s->look = (lzo_uint) (s->c->in_end - s->c->ip);
++      if (s->look > 0)
++      {
++              if (s->look > s->f)
++                      s->look = s->f;
++              memcpy (&s->b[s->ip], s->c->ip, s->look);
++              s->c->ip += s->look;
++              s->ip += s->look;
++      }
++
++      if (s->ip == s->b_size)
++              s->ip = 0;
++
++      if (s->look >= 2 && s->dict_len > 0)
++              swd_insertdict (s, 0, s->dict_len);
++
++      s->rp = s->first_rp;
++      if (s->rp >= s->node_count)
++              s->rp -= s->node_count;
++      else
++              s->rp += s->b_size - s->node_count;
++
++      return LZO_E_OK;
++}
++
++static __inline__ void
++swd_remove_node (lzo1x_999_swd_t * s, lzo_uint node)
++{
++      if (s->node_count == 0)
++      {
++              lzo_uint key;
++
++              key = HEAD3 (s->b, node);
++
++              --s->llen3[key];
++
++              key = HEAD2 (s->b, node);
++
++              if ((lzo_uint) s->head2[key] == node)
++                      s->head2[key] = NIL2;
++      }
++      else
++              --s->node_count;
++}
++
++static void
++swd_accept (lzo1x_999_swd_t * s, lzo_uint n)
++{
++
++      while (n--)
++      {
++              lzo_uint key;
++
++              swd_remove_node (s, s->rp);
++
++              key = HEAD3 (s->b, s->bp);
++              s->succ3[s->bp] = s_head3 (s, key);
++              s->head3[key] = SWD_UINT (s->bp);
++              s->best3[s->bp] = SWD_UINT (s->f + 1);
++              s->llen3[key]++;
++
++              key = HEAD2 (s->b, s->bp);
++              s->head2[key] = SWD_UINT (s->bp);;
++
++              swd_getbyte (s);
++      }
++}
++
++static void
++swd_search (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint cnt)
++{
++      const unsigned char *p1;
++      const unsigned char *p2;
++      const unsigned char *px;
++
++      lzo_uint m_len = s->m_len;
++      const unsigned char *b = s->b;
++      const unsigned char *bp = s->b + s->bp;
++      const unsigned char *bx = s->b + s->bp + s->look;
++      unsigned char scan_end1;
++
++      scan_end1 = bp[m_len - 1];
++      for (; cnt-- > 0; node = s->succ3[node])
++      {
++              p1 = bp;
++              p2 = b + node;
++              px = bx;
++
++              if (p2[m_len - 1] == scan_end1 &&
++                  p2[m_len] == p1[m_len] &&
++                  p2[0] == p1[0] && p2[1] == p1[1])
++              {
++                      lzo_uint i;
++
++                      p1 += 2;
++                      p2 += 2;
++                      do
++                      {
++                      }
++                      while (++p1 < px && *p1 == *++p2);
++
++                      i = p1 - bp;
++
++                      if (i < SWD_BEST_OFF)
++                      {
++                              if (s->best_pos[i] == 0)
++                                      s->best_pos[i] = node + 1;
++                      }
++
++                      if (i > m_len)
++                      {
++                              s->m_len = m_len = i;
++                              s->m_pos = node;
++                              if (m_len == s->look)
++                                      return;
++                              if (m_len >= s->nice_length)
++                                      return;
++                              if (m_len > (lzo_uint) s->best3[node])
++                                      return;
++                              scan_end1 = bp[m_len - 1];
++                      }
++              }
++      }
++}
++
++static lzo_bool
++swd_search2 (lzo1x_999_swd_t * s)
++{
++      lzo_uint key;
++
++      key = s->head2[HEAD2 (s->b, s->bp)];
++      if (key == NIL2)
++              return 0;
++
++      if (s->best_pos[2] == 0)
++              s->best_pos[2] = key + 1;
++
++      if (s->m_len < 2)
++      {
++              s->m_len = 2;
++              s->m_pos = key;
++      }
++      return 1;
++}
++
++static void
++swd_findbest (lzo1x_999_swd_t * s)
++{
++      lzo_uint key;
++      lzo_uint cnt, node;
++      lzo_uint len;
++
++      key = HEAD3 (s->b, s->bp);
++      node = s->succ3[s->bp] = s_head3 (s, key);
++      cnt = s->llen3[key]++;
++
++      if (cnt > s->max_chain && s->max_chain > 0)
++              cnt = s->max_chain;
++      s->head3[key] = SWD_UINT (s->bp);
++
++      s->b_char = s->b[s->bp];
++      len = s->m_len;
++      if (s->m_len >= s->look)
++      {
++              if (s->look == 0)
++                      s->b_char = -1;
++              s->m_off = 0;
++              s->best3[s->bp] = SWD_UINT (s->f + 1);
++      }
++      else
++      {
++
++              if (swd_search2 (s))
++
++                      if (s->look >= 3)
++                              swd_search (s, node, cnt);
++              if (s->m_len > len)
++                      s->m_off = swd_pos2off (s, s->m_pos);
++              s->best3[s->bp] = SWD_UINT (s->m_len);
++
++              if (s->use_best_off)
++              {
++                      int i;
++                      for (i = 2; i < SWD_BEST_OFF; i++)
++                              if (s->best_pos[i] > 0)
++                                      s->best_off[i] =
++                                              swd_pos2off (s,
++                                                           s->best_pos[i] -
++                                                           1);
++                              else
++                                      s->best_off[i] = 0;
++              }
++
++      }
++
++      swd_remove_node (s, s->rp);
++
++      key = HEAD2 (s->b, s->bp);
++      s->head2[key] = SWD_UINT (s->bp);
++
++}
++
++/* lzo_mchw.ch */
++
++static int
++init_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
++          const lzo_byte * dict, lzo_uint dict_len, lzo_uint32 flags)
++{
++      int r;
++
++      c->init = 1;
++
++      s->c = c;
++
++      c->last_m_len = c->last_m_off = 0;
++
++      c->textsize = c->codesize = c->printcount = 0;
++      c->lit_bytes = c->match_bytes = c->rep_bytes = 0;
++      c->lazy = 0;
++
++      r = swd_init (s, dict, dict_len);
++      if (r != 0)
++              return r;
++
++      s->use_best_off = (flags & 1) ? 1 : 0;
++      return r;
++}
++
++static int
++find_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
++          lzo_uint this_len, lzo_uint skip)
++{
++      if (skip > 0)
++      {
++              swd_accept (s, this_len - skip);
++              c->textsize += this_len - skip + 1;
++      }
++      else
++      {
++              c->textsize += this_len - skip;
++      }
++
++      s->m_len = 1;
++      s->m_len = 1;
++
++      if (s->use_best_off)
++              memset (s->best_pos, 0, sizeof (s->best_pos));
++
++      swd_findbest (s);
++      c->m_len = s->m_len;
++      c->m_off = s->m_off;
++
++      swd_getbyte (s);
++
++      if (s->b_char < 0)
++      {
++              c->look = 0;
++              c->m_len = 0;
++      }
++      else
++      {
++              c->look = s->look + 1;
++      }
++      c->bp = c->ip - c->look;
++
++      if (c->cb && c->textsize > c->printcount)
++      {
++              (*c->cb) (c->textsize, c->codesize);
++              c->printcount += 1024;
++      }
++
++      return LZO_E_OK;
++}
++
++/* lzo1x_9x.c */
++
++static lzo_byte *
++code_match (lzo1x_999_t * c, lzo_byte * op, lzo_uint m_len, lzo_uint m_off)
++{
++      lzo_uint x_len = m_len;
++      lzo_uint x_off = m_off;
++
++      c->match_bytes += m_len;
++
++      if (m_len == 2)
++      {
++              m_off -= 1;
++
++              *op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
++              *op++ = LZO_BYTE (m_off >> 2);
++
++              c->m1a_m++;
++      }
++
++      else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
++
++      {
++
++              m_off -= 1;
++              *op++ = LZO_BYTE (((m_len - 1) << 5) | ((m_off & 7) << 2));
++              *op++ = LZO_BYTE (m_off >> 3);
++              c->m2_m++;
++      }
++      else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET
++               && c->r1_lit >= 4)
++      {
++              m_off -= 1 + M2_MAX_OFFSET;
++
++              *op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
++              *op++ = LZO_BYTE (m_off >> 2);
++
++              c->m1b_m++;
++      }
++      else if (m_off <= M3_MAX_OFFSET)
++      {
++              m_off -= 1;
++              if (m_len <= M3_MAX_LEN)
++                      *op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
++              else
++              {
++                      m_len -= M3_MAX_LEN;
++                      *op++ = M3_MARKER | 0;
++                      while (m_len > 255)
++                      {
++                              m_len -= 255;
++                              *op++ = 0;
++                      }
++                      *op++ = LZO_BYTE (m_len);
++              }
++
++              *op++ = LZO_BYTE (m_off << 2);
++              *op++ = LZO_BYTE (m_off >> 6);
++
++              c->m3_m++;
++      }
++      else
++      {
++              lzo_uint k;
++
++              m_off -= 0x4000;
++              k = (m_off & 0x4000) >> 11;
++              if (m_len <= M4_MAX_LEN)
++                      *op++ = LZO_BYTE (M4_MARKER | k | (m_len - 2));
++              else
++              {
++                      m_len -= M4_MAX_LEN;
++                      *op++ = LZO_BYTE (M4_MARKER | k | 0);
++                      while (m_len > 255)
++                      {
++                              m_len -= 255;
++                              *op++ = 0;
++                      }
++                      *op++ = LZO_BYTE (m_len);
++              }
++
++              *op++ = LZO_BYTE (m_off << 2);
++              *op++ = LZO_BYTE (m_off >> 6);
++
++              c->m4_m++;
++      }
++
++      c->last_m_len = x_len;
++      c->last_m_off = x_off;
++      return op;
++}
++
++static lzo_byte *
++STORE_RUN (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii, lzo_uint t)
++{
++      c->lit_bytes += t;
++
++      if (op == c->out && t <= 238)
++      {
++              *op++ = LZO_BYTE (17 + t);
++      }
++      else if (t <= 3)
++      {
++              op[-2] |= LZO_BYTE (t);
++
++              c->lit1_r++;
++      }
++      else if (t <= 18)
++      {
++              *op++ = LZO_BYTE (t - 3);
++              c->lit2_r++;
++      }
++      else
++      {
++              lzo_uint tt = t - 18;
++
++              *op++ = 0;
++              while (tt > 255)
++              {
++                      tt -= 255;
++                      *op++ = 0;
++              }
++              *op++ = LZO_BYTE (tt);
++              c->lit3_r++;
++      }
++      do
++              *op++ = *ii++;
++      while (--t > 0);
++
++      return op;
++}
++
++static lzo_byte *
++code_run (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii,
++        lzo_uint lit, lzo_uint m_len)
++{
++      if (lit > 0)
++      {
++              op = STORE_RUN (c, op, ii, lit);
++              c->r1_m_len = m_len;
++              c->r1_lit = lit;
++      }
++      else
++      {
++              c->r1_m_len = 0;
++              c->r1_lit = 0;
++      }
++
++      return op;
++}
++
++static int
++len_of_coded_match (lzo_uint m_len, lzo_uint m_off, lzo_uint lit)
++{
++      int n = 4;
++
++      if (m_len < 2)
++              return -1;
++      if (m_len == 2)
++              return (m_off <= M1_MAX_OFFSET && lit > 0
++                      && lit < 4) ? 2 : -1;
++      if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
++              return 2;
++      if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4)
++              return 2;
++      if (m_off <= M3_MAX_OFFSET)
++      {
++              if (m_len <= M3_MAX_LEN)
++                      return 3;
++              m_len -= M3_MAX_LEN;
++              while (m_len > 255)
++              {
++                      m_len -= 255;
++                      n++;
++              }
++              return n;
++      }
++      if (m_off <= M4_MAX_OFFSET)
++      {
++              if (m_len <= M4_MAX_LEN)
++                      return 3;
++              m_len -= M4_MAX_LEN;
++              while (m_len > 255)
++              {
++                      m_len -= 255;
++                      n++;
++              }
++              return n;
++      }
++      return -1;
++}
++
++static lzo_int
++min_gain (lzo_uint ahead, lzo_uint lit1, lzo_uint lit2, int l1, int l2,
++        int l3)
++{
++      lzo_int lazy_match_min_gain = 0;
++
++      lazy_match_min_gain += ahead;
++
++      if (lit1 <= 3)
++              lazy_match_min_gain += (lit2 <= 3) ? 0 : 2;
++      else if (lit1 <= 18)
++              lazy_match_min_gain += (lit2 <= 18) ? 0 : 1;
++
++      lazy_match_min_gain += (l2 - l1) * 2;
++      if (l3 > 0)
++              lazy_match_min_gain -= (ahead - l3) * 2;
++
++      if (lazy_match_min_gain < 0)
++              lazy_match_min_gain = 0;
++
++      return lazy_match_min_gain;
++}
++
++static void
++better_match (const lzo1x_999_swd_t * swd, lzo_uint * m_len, lzo_uint * m_off)
++{
++      if (*m_len <= M2_MIN_LEN)
++              return;
++
++      if (*m_off <= M2_MAX_OFFSET)
++              return;
++
++      if (*m_off > M2_MAX_OFFSET &&
++          *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 &&
++          swd->best_off[*m_len - 1]
++          && swd->best_off[*m_len - 1] <= M2_MAX_OFFSET)
++      {
++              *m_len = *m_len - 1;
++              *m_off = swd->best_off[*m_len];
++              return;
++      }
++
++      if (*m_off > M3_MAX_OFFSET &&
++          *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 &&
++          swd->best_off[*m_len - 2]
++          && swd->best_off[*m_len - 2] <= M2_MAX_OFFSET)
++      {
++              *m_len = *m_len - 2;
++              *m_off = swd->best_off[*m_len];
++              return;
++      }
++
++      if (*m_off > M3_MAX_OFFSET &&
++          *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 &&
++          swd->best_off[*m_len - 1]
++          && swd->best_off[*m_len - 1] <= M3_MAX_OFFSET)
++      {
++              *m_len = *m_len - 1;
++              *m_off = swd->best_off[*m_len];
++      }
++
++}
++
++/* minilzo.c */
++
++static lzo_bool
++lzo_assert (int expr)
++{
++      return (expr) ? 1 : 0;
++}
++
++/* lzo1x_9x.c */
++
++static int
++lzo1x_999_compress_internal (const lzo_byte * in, lzo_uint in_len,
++                           lzo_byte * out, lzo_uintp out_len,
++                           lzo_voidp wrkmem,
++                           const lzo_byte * dict, lzo_uint dict_len,
++                           lzo_progress_callback_t cb,
++                           int try_lazy,
++                           lzo_uint good_length,
++                           lzo_uint max_lazy,
++                           lzo_uint nice_length,
++                           lzo_uint max_chain, lzo_uint32 flags)
++{
++      lzo_byte *op;
++      const lzo_byte *ii;
++      lzo_uint lit;
++      lzo_uint m_len, m_off;
++      lzo1x_999_t cc;
++      lzo1x_999_t *const c = &cc;
++      lzo1x_999_swd_t *const swd = (lzo1x_999_swd_t *) wrkmem;
++      int r;
++
++      if (!lzo_assert
++          (LZO1X_999_MEM_COMPRESS >= lzo_sizeof (lzo1x_999_swd_t)))
++              return LZO_E_ERROR;
++
++      if (try_lazy < 0)
++              try_lazy = 1;
++
++      if (good_length <= 0)
++              good_length = 32;
++
++      if (max_lazy <= 0)
++              max_lazy = 32;
++
++      if (nice_length <= 0)
++              nice_length = 0;
++
++      if (max_chain <= 0)
++              max_chain = SWD_MAX_CHAIN;
++
++      c->init = 0;
++      c->ip = c->in = in;
++      c->in_end = in + in_len;
++      c->out = out;
++      c->cb = cb;
++      c->m1a_m = c->m1b_m = c->m2_m = c->m3_m = c->m4_m = 0;
++      c->lit1_r = c->lit2_r = c->lit3_r = 0;
++
++      op = out;
++      ii = c->ip;
++      lit = 0;
++      c->r1_lit = c->r1_m_len = 0;
++
++      r = init_match (c, swd, dict, dict_len, flags);
++      if (r != 0)
++              return r;
++      if (max_chain > 0)
++              swd->max_chain = max_chain;
++      if (nice_length > 0)
++              swd->nice_length = nice_length;
++
++      r = find_match (c, swd, 0, 0);
++      if (r != 0)
++              return r;
++      while (c->look > 0)
++      {
++              lzo_uint ahead;
++              lzo_uint max_ahead;
++              int l1, l2, l3;
++
++              c->codesize = op - out;
++
++              m_len = c->m_len;
++              m_off = c->m_off;
++
++              if (lit == 0)
++                      ii = c->bp;
++
++              if (m_len < 2 ||
++                  (m_len == 2
++                   && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4))
++                  || (m_len == 2 && op == out) || (op == out && lit == 0))
++              {
++
++                      m_len = 0;
++              }
++              else if (m_len == M2_MIN_LEN)
++              {
++
++                      if (m_off > MX_MAX_OFFSET && lit >= 4)
++                              m_len = 0;
++              }
++
++              if (m_len == 0)
++              {
++
++                      lit++;
++                      swd->max_chain = max_chain;
++                      r = find_match (c, swd, 1, 0);
++                      continue;
++              }
++
++              if (swd->use_best_off)
++                      better_match (swd, &m_len, &m_off);
++
++              ahead = 0;
++              if (try_lazy <= 0 || m_len >= max_lazy)
++              {
++
++                      l1 = 0;
++                      max_ahead = 0;
++              }
++              else
++              {
++
++                      l1 = len_of_coded_match (m_len, m_off, lit);
++
++                      max_ahead = LZO_MIN (try_lazy, l1 - 1);
++
++              }
++
++              while (ahead < max_ahead && c->look > m_len)
++              {
++                      lzo_int lazy_match_min_gain;
++
++                      if (m_len >= good_length)
++                              swd->max_chain = max_chain >> 2;
++                      else
++                              swd->max_chain = max_chain;
++                      r = find_match (c, swd, 1, 0);
++                      ahead++;
++
++                      if (c->m_len < m_len)
++                              continue;
++
++                      if (c->m_len == m_len && c->m_off >= m_off)
++                              continue;
++
++                      if (swd->use_best_off)
++                              better_match (swd, &c->m_len, &c->m_off);
++
++                      l2 = len_of_coded_match (c->m_len, c->m_off,
++                                               lit + ahead);
++                      if (l2 < 0)
++                              continue;
++
++                      l3 = (op == out) ? -1 : len_of_coded_match (ahead,
++                                                                  m_off,
++                                                                  lit);
++
++                      lazy_match_min_gain =
++                              min_gain (ahead, lit, lit + ahead, l1, l2,
++                                        l3);
++                      if (c->m_len >= m_len + lazy_match_min_gain)
++                      {
++                              c->lazy++;
++
++                              if (l3 > 0)
++                              {
++
++                                      op = code_run (c, op, ii, lit, ahead);
++                                      lit = 0;
++
++                                      op = code_match (c, op, ahead, m_off);
++                              }
++                              else
++                              {
++                                      lit += ahead;
++                              }
++                              goto lazy_match_done;
++                      }
++              }
++
++              op = code_run (c, op, ii, lit, m_len);
++              lit = 0;
++
++              op = code_match (c, op, m_len, m_off);
++              swd->max_chain = max_chain;
++              r = find_match (c, swd, m_len, 1 + ahead);
++
++            lazy_match_done:;
++      }
++
++      if (lit > 0)
++              op = STORE_RUN (c, op, ii, lit);
++
++      *op++ = M4_MARKER | 1;
++      *op++ = 0;
++      *op++ = 0;
++
++      c->codesize = op - out;
++
++      *out_len = op - out;
++
++      if (c->cb)
++              (*c->cb) (c->textsize, c->codesize);
++
++      return LZO_E_OK;
++}
++
++static int
++lzo1x_999_compress_level (const lzo_byte * in, lzo_uint in_len,
++                        lzo_byte * out, lzo_uintp out_len,
++                        lzo_voidp wrkmem,
++                        const lzo_byte * dict, lzo_uint dict_len,
++                        lzo_progress_callback_t cb, int compression_level)
++{
++      static const struct
++      {
++              int try_lazy;
++              lzo_uint good_length;
++              lzo_uint max_lazy;
++              lzo_uint nice_length;
++              lzo_uint max_chain;
++              lzo_uint32 flags;
++      } c[9] =
++      {
++              {
++              0, 0, 0, 8, 4, 0},
++              {
++              0, 0, 0, 16, 8, 0},
++              {
++              0, 0, 0, 32, 16, 0},
++              {
++              1, 4, 4, 16, 16, 0},
++              {
++              1, 8, 16, 32, 32, 0},
++              {
++              1, 8, 16, 128, 128, 0},
++              {
++              2, 8, 32, 128, 256, 0},
++              {
++              2, 32, 128, F, 2048, 1},
++              {
++              2, F, F, F, 4096, 1}
++      };
++
++      if (compression_level < 1 || compression_level > 9)
++              return LZO_E_ERROR;
++
++      compression_level -= 1;
++      return lzo1x_999_compress_internal (in, in_len, out, out_len, wrkmem,
++                                          dict, dict_len, cb,
++                                          c[compression_level].try_lazy,
++                                          c[compression_level].good_length,
++                                          c[compression_level].max_lazy,
++                                          0,
++                                          c[compression_level].max_chain,
++                                          c[compression_level].flags);
++}
++
++static int
++lzo1x_999_compress (const lzo_byte * in, lzo_uint in_len,
++                  lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++      return lzo1x_999_compress_level (in, in_len, out, out_len, wrkmem,
++                                       NULL, 0, 0, 8);
++}
++
++/* minilzo.c */
++
++#ifdef JFFS2_LZO_1
++static const lzo_byte __lzo_copyright[] = LZO_VERSION_STRING;
++
++static lzo_uint
++_lzo1x_1_do_compress (const lzo_byte * in, lzo_uint in_len,
++                    lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++
++      register const lzo_byte *ip;
++
++      lzo_byte *op;
++      const lzo_byte *const in_end = in + in_len;
++      const lzo_byte *const ip_end = in + in_len - 8 - 5;
++      const lzo_byte *ii;
++      lzo_dict_p const dict = (lzo_dict_p) wrkmem;
++
++      op = out;
++      ip = in;
++      ii = ip;
++
++      ip += 4;
++      for (;;)
++      {
++              register const lzo_byte *m_pos;
++
++              lzo_uint m_off;
++              lzo_uint m_len;
++              lzo_uint dindex;
++
++              DINDEX1 (dindex, ip);
++              GINDEX (m_pos, m_off, dict, dindex, in);
++              if (LZO_CHECK_MPOS_NON_DET
++                  (m_pos, m_off, in, ip, M4_MAX_OFFSET))
++                      goto literal;
++
++              if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
++                      goto try_match;
++              DINDEX2 (dindex, ip);
++              GINDEX (m_pos, m_off, dict, dindex, in);
++
++              if (LZO_CHECK_MPOS_NON_DET
++                  (m_pos, m_off, in, ip, M4_MAX_OFFSET))
++                      goto literal;
++              if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
++                      goto try_match;
++              goto literal;
++
++            try_match:
++              if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
++              {
++              }
++              else
++              {
++                      if (m_pos[2] == ip[2])
++                      {
++                              goto match;
++                      }
++                      else
++                      {
++                      }
++              }
++
++            literal:
++              UPDATE_I (dict, 0, dindex, ip, in);
++              ++ip;
++              if (ip >= ip_end)
++                      break;
++              continue;
++
++            match:
++              UPDATE_I (dict, 0, dindex, ip, in);
++
++              if (pd (ip, ii) > 0)
++              {
++                      register lzo_uint t = pd (ip, ii);
++
++                      if (t <= 3)
++                      {
++                              op[-2] |= LZO_BYTE (t);
++                      }
++                      else if (t <= 18)
++                              *op++ = LZO_BYTE (t - 3);
++                      else
++                      {
++                              register lzo_uint tt = t - 18;
++
++                              *op++ = 0;
++                              while (tt > 255)
++                              {
++                                      tt -= 255;
++                                      *op++ = 0;
++                              }
++                              *op++ = LZO_BYTE (tt);;
++                      }
++                      do
++                              *op++ = *ii++;
++                      while (--t > 0);
++              }
++
++              ip += 3;
++              if (m_pos[3] != *ip++ || m_pos[4] != *ip++
++                  || m_pos[5] != *ip++ || m_pos[6] != *ip++
++                  || m_pos[7] != *ip++ || m_pos[8] != *ip++)
++              {
++                      --ip;
++                      m_len = ip - ii;
++
++                      if (m_off <= M2_MAX_OFFSET)
++                      {
++                              m_off -= 1;
++
++                              *op++ = LZO_BYTE (((m_len -
++                                                  1) << 5) | ((m_off & 7) <<
++                                                              2));
++                              *op++ = LZO_BYTE (m_off >> 3);
++                      }
++                      else if (m_off <= M3_MAX_OFFSET)
++                      {
++                              m_off -= 1;
++                              *op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
++                              goto m3_m4_offset;
++                      }
++                      else
++
++                      {
++                              m_off -= 0x4000;
++
++                              *op++ = LZO_BYTE (M4_MARKER |
++                                                ((m_off & 0x4000) >> 11) |
++                                                (m_len - 2));
++                              goto m3_m4_offset;
++                      }
++              }
++              else
++              {
++                      {
++                              const lzo_byte *end = in_end;
++                              const lzo_byte *m = m_pos + M2_MAX_LEN + 1;
++                              while (ip < end && *m == *ip)
++                                      m++, ip++;
++                              m_len = (ip - ii);
++                      }
++
++
++                      if (m_off <= M3_MAX_OFFSET)
++                      {
++                              m_off -= 1;
++                              if (m_len <= 33)
++                                      *op++ = LZO_BYTE (M3_MARKER |
++                                                        (m_len - 2));
++                              else
++                              {
++                                      m_len -= 33;
++                                      *op++ = M3_MARKER | 0;
++                                      goto m3_m4_len;
++                              }
++                      }
++                      else
++                      {
++                              m_off -= 0x4000;
++
++                              if (m_len <= M4_MAX_LEN)
++                                      *op++ = LZO_BYTE (M4_MARKER |
++                                                        ((m_off & 0x4000) >>
++                                                         11) | (m_len - 2));
++
++                              else
++                              {
++                                      m_len -= M4_MAX_LEN;
++                                      *op++ = LZO_BYTE (M4_MARKER |
++                                                        ((m_off & 0x4000) >>
++                                                         11));
++                                    m3_m4_len:
++                                      while (m_len > 255)
++                                      {
++                                              m_len -= 255;
++                                              *op++ = 0;
++                                      }
++
++                                      *op++ = LZO_BYTE (m_len);
++                              }
++                      }
++
++                    m3_m4_offset:
++                      *op++ = LZO_BYTE ((m_off & 63) << 2);
++                      *op++ = LZO_BYTE (m_off >> 6);
++              }
++              ii = ip;
++              if (ip >= ip_end)
++                      break;
++      }
++
++      *out_len = op - out;
++      return pd (in_end, ii);
++}
++#endif
++
++#ifdef JFFS2_LZO_1
++static int
++lzo1x_1_compress (const lzo_byte * in, lzo_uint in_len,
++                lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++      lzo_byte *op = out;
++      lzo_uint t;
++
++      if (in_len <= M2_MAX_LEN + 5)
++              t = in_len;
++      else
++      {
++              t = _lzo1x_1_do_compress (in, in_len, op, out_len, wrkmem);
++              op += *out_len;
++      }
++
++      if (t > 0)
++      {
++              const lzo_byte *ii = in + in_len - t;
++
++              if (op == out && t <= 238)
++                      *op++ = LZO_BYTE (17 + t);
++              else if (t <= 3)
++                      op[-2] |= LZO_BYTE (t);
++              else if (t <= 18)
++                      *op++ = LZO_BYTE (t - 3);
++              else
++              {
++                      lzo_uint tt = t - 18;
++
++                      *op++ = 0;
++                      while (tt > 255)
++                      {
++                              tt -= 255;
++                              *op++ = 0;
++                      }
++
++                      *op++ = LZO_BYTE (tt);
++              }
++              do
++                      *op++ = *ii++;
++              while (--t > 0);
++      }
++
++      *op++ = M4_MARKER | 1;
++      *op++ = 0;
++      *op++ = 0;
++
++      *out_len = op - out;
++      return 0;
++}
++#endif
++
++static int
++lzo1x_decompress (const lzo_byte * in, lzo_uint in_len,
++                lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++      register lzo_byte *op;
++      register const lzo_byte *ip;
++      register lzo_uint t;
++
++      register const lzo_byte *m_pos;
++
++      const lzo_byte *const ip_end = in + in_len;
++      lzo_byte *const op_end = out + *out_len;
++
++      *out_len = 0;
++
++      op = out;
++      ip = in;
++
++      if (*ip > 17)
++      {
++              t = *ip++ - 17;
++              if (t < 4)
++                      goto match_next;
++              NEED_OP (t);
++              NEED_IP (t + 1);
++              do
++                      *op++ = *ip++;
++              while (--t > 0);
++              goto first_literal_run;
++      }
++
++      while (TEST_IP && TEST_OP)
++      {
++              t = *ip++;
++              if (t >= 16)
++                      goto match;
++              if (t == 0)
++              {
++                      NEED_IP (1);
++                      while (*ip == 0)
++                      {
++                              t += 255;
++                              ip++;
++                              NEED_IP (1);
++                      }
++                      t += 15 + *ip++;
++              }
++              NEED_OP (t + 3);
++              NEED_IP (t + 4);
++              if (PTR_ALIGNED2_4 (op, ip))
++              {
++                      COPY4 (op, ip);
++
++                      op += 4;
++                      ip += 4;
++                      if (--t > 0)
++                      {
++                              if (t >= 4)
++                              {
++                                      do
++                                      {
++                                              COPY4 (op, ip);
++                                              op += 4;
++                                              ip += 4;
++                                              t -= 4;
++                                      }
++                                      while (t >= 4);
++                                      if (t > 0)
++                                              do
++                                                      *op++ = *ip++;
++                                              while (--t > 0);
++                              }
++                              else
++                                      do
++                                              *op++ = *ip++;
++                                      while (--t > 0);
++                      }
++              }
++              else
++              {
++                      *op++ = *ip++;
++                      *op++ = *ip++;
++                      *op++ = *ip++;
++                      do
++                              *op++ = *ip++;
++                      while (--t > 0);
++              }
++            first_literal_run:
++
++              t = *ip++;
++              if (t >= 16)
++                      goto match;
++
++              m_pos = op - (1 + M2_MAX_OFFSET);
++              m_pos -= t >> 2;
++              m_pos -= *ip++ << 2;
++              TEST_LOOKBEHIND (m_pos, out);
++              NEED_OP (3);
++              *op++ = *m_pos++;
++              *op++ = *m_pos++;
++              *op++ = *m_pos;
++
++              goto match_done;
++
++              while (TEST_IP && TEST_OP)
++              {
++                    match:
++                      if (t >= 64)
++                      {
++                              m_pos = op - 1;
++                              m_pos -= (t >> 2) & 7;
++                              m_pos -= *ip++ << 3;
++                              t = (t >> 5) - 1;
++                              TEST_LOOKBEHIND (m_pos, out);
++                              NEED_OP (t + 3 - 1);
++                              goto copy_match;
++
++                      }
++                      else if (t >= 32)
++                      {
++                              t &= 31;
++                              if (t == 0)
++                              {
++                                      NEED_IP (1);
++                                      while (*ip == 0)
++                                      {
++                                              t += 255;
++                                              ip++;
++                                              NEED_IP (1);
++                                      }
++                                      t += 31 + *ip++;
++                              }
++
++                              m_pos = op - 1;
++                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++                              ip += 2;
++                      }
++                      else if (t >= 16)
++                      {
++                              m_pos = op;
++                              m_pos -= (t & 8) << 11;
++
++                              t &= 7;
++                              if (t == 0)
++                              {
++                                      NEED_IP (1);
++                                      while (*ip == 0)
++                                      {
++                                              t += 255;
++                                              ip++;
++                                              NEED_IP (1);
++                                      }
++                                      t += 7 + *ip++;
++                              }
++
++                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++                              ip += 2;
++                              if (m_pos == op)
++                                      goto eof_found;
++                              m_pos -= 0x4000;
++                      }
++                      else
++                      {
++
++                              m_pos = op - 1;
++                              m_pos -= t >> 2;
++                              m_pos -= *ip++ << 2;
++                              TEST_LOOKBEHIND (m_pos, out);
++                              NEED_OP (2);
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos;
++
++                              goto match_done;
++                      }
++
++                      TEST_LOOKBEHIND (m_pos, out);
++                      NEED_OP (t + 3 - 1);
++                      if (t >= 2 * 4 - (3 - 1)
++                          && PTR_ALIGNED2_4 (op, m_pos))
++                      {
++                              COPY4 (op, m_pos);
++                              op += 4;
++                              m_pos += 4;
++                              t -= 4 - (3 - 1);
++                              do
++                              {
++                                      COPY4 (op, m_pos);
++                                      op += 4;
++                                      m_pos += 4;
++                                      t -= 4;
++                              }
++                              while (t >= 4);
++                              if (t > 0)
++                                      do
++                                              *op++ = *m_pos++;
++                                      while (--t > 0);
++                      }
++                      else
++
++                      {
++                            copy_match:
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos++;
++                              do
++                                      *op++ = *m_pos++;
++                              while (--t > 0);
++                      }
++
++                    match_done:
++                      t = ip[-2] & 3;
++
++                      if (t == 0)
++                              break;
++
++                    match_next:
++                      NEED_OP (t);
++                      NEED_IP (t + 1);
++                      do
++                              *op++ = *ip++;
++                      while (--t > 0);
++                      t = *ip++;
++              }
++      }
++      *out_len = op - out;
++      return LZO_E_EOF_NOT_FOUND;
++
++      eof_found:
++      *out_len = op - out;
++      return (ip == ip_end ? LZO_E_OK :
++              (ip <
++               ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++
++      input_overrun:
++      *out_len = op - out;
++      return LZO_E_INPUT_OVERRUN;
++
++      output_overrun:
++      *out_len = op - out;
++      return LZO_E_OUTPUT_OVERRUN;
++
++      lookbehind_overrun:
++      *out_len = op - out;
++      return LZO_E_LOOKBEHIND_OVERRUN;
++}
++
++/* lzo1x_oo.ch */
++
++#define NO_LIT          LZO_UINT_MAX
++
++static void
++copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++      ip[0] = m_pos[0];
++      if (off == 1)
++              ip[1] = m_pos[0];
++      else
++              ip[1] = m_pos[1];
++}
++
++static void
++copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++      ip[0] = m_pos[0];
++      if (off == 1)
++      {
++              ip[2] = ip[1] = m_pos[0];
++      }
++      else if (off == 2)
++      {
++              ip[1] = m_pos[1];
++              ip[2] = m_pos[0];
++      }
++      else
++      {
++              ip[1] = m_pos[1];
++              ip[2] = m_pos[2];
++      }
++}
++
++static int
++lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
++              lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++      register lzo_byte *op;
++      register lzo_byte *ip;
++      register lzo_uint t;
++      register lzo_byte *m_pos;
++      lzo_uint nl;
++      const lzo_byte *const ip_end = in + in_len;
++      const lzo_byte *const op_end = out + *out_len;
++      lzo_byte *litp = NULL;
++      lzo_uint lit = 0;
++      lzo_uint next_lit = NO_LIT;
++      long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
++
++      *out_len = 0;
++
++      op = out;
++      ip = in;
++
++      if (*ip > 17)
++      {
++              t = *ip++ - 17;
++              if (t < 4)
++                      goto match_next;
++              goto first_literal_run;
++      }
++
++      while (TEST_IP && TEST_OP)
++      {
++              t = *ip++;
++              if (t >= 16)
++                      goto match;
++              litp = ip - 1;
++              if (t == 0)
++              {
++                      t = 15;
++                      while (*ip == 0)
++                              t += 255, ip++;
++                      t += *ip++;
++              }
++              lit = t + 3;
++            copy_literal_run:
++              *op++ = *ip++;
++              *op++ = *ip++;
++              *op++ = *ip++;
++            first_literal_run:
++              do
++                      *op++ = *ip++;
++              while (--t > 0);
++
++              t = *ip++;
++
++              if (t >= 16)
++                      goto match;
++              m_pos = op - 1 - 0x800;
++              m_pos -= t >> 2;
++              m_pos -= *ip++ << 2;
++              *op++ = *m_pos++;
++              *op++ = *m_pos++;
++              *op++ = *m_pos++;
++              lit = 0;
++              goto match_done;
++
++              while (TEST_IP && TEST_OP)
++              {
++                      if (t < 16)
++                      {
++                              m_pos = op - 1;
++                              m_pos -= t >> 2;
++                              m_pos -= *ip++ << 2;
++
++                              if (litp == NULL)
++                                      goto copy_m1;
++
++                              nl = ip[-2] & 3;
++                              if (nl == 0 && lit == 1 && ip[0] >= 16)
++                              {
++                                      next_lit = nl;
++                                      lit += 2;
++                                      *litp = LZO_BYTE ((*litp & ~3) | lit);
++                                      copy2 (ip - 2, m_pos, op - m_pos);
++                                      o_m1_a++;
++                              }
++                              else if (nl == 0 && ip[0] < 16 && ip[0] != 0
++                                       && (lit + 2 + ip[0] < 16))
++                              {
++                                      t = *ip++;
++                                      *litp &= ~3;
++                                      copy2 (ip - 3 + 1, m_pos, op - m_pos);
++                                      litp += 2;
++                                      if (lit > 0)
++                                              memmove (litp + 1, litp, lit);
++                                      lit += 2 + t + 3;
++                                      *litp = LZO_BYTE (lit - 3);
++
++                                      o_m1_b++;
++                                      *op++ = *m_pos++;
++                                      *op++ = *m_pos++;
++                                      goto copy_literal_run;
++                              }
++                            copy_m1:
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos++;
++                      }
++                      else
++                      {
++                            match:
++                              if (t >= 64)
++                              {
++                                      m_pos = op - 1;
++                                      m_pos -= (t >> 2) & 7;
++                                      m_pos -= *ip++ << 3;
++                                      t = (t >> 5) - 1;
++                                      if (litp == NULL)
++                                              goto copy_m;
++
++                                      nl = ip[-2] & 3;
++                                      if (t == 1 && lit > 3 && nl == 0 &&
++                                          ip[0] < 16 && ip[0] != 0
++                                          && (lit + 3 + ip[0] < 16))
++                                      {
++                                              t = *ip++;
++                                              copy3 (ip - 1 - 2, m_pos,
++                                                     op - m_pos);
++                                              lit += 3 + t + 3;
++                                              *litp = LZO_BYTE (lit - 3);
++                                              o_m2++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              goto copy_literal_run;
++                                      }
++                              }
++                              else
++                              {
++                                      if (t >= 32)
++                                      {
++                                              t &= 31;
++                                              if (t == 0)
++                                              {
++                                                      t = 31;
++                                                      while (*ip == 0)
++                                                              t += 255,
++                                                                      ip++;
++                                                      t += *ip++;
++                                              }
++                                              m_pos = op - 1;
++                                              m_pos -= *ip++ >> 2;
++                                              m_pos -= *ip++ << 6;
++                                      }
++                                      else
++                                      {
++                                              m_pos = op;
++                                              m_pos -= (t & 8) << 11;
++                                              t &= 7;
++                                              if (t == 0)
++                                              {
++                                                      t = 7;
++                                                      while (*ip == 0)
++                                                              t += 255,
++                                                                      ip++;
++                                                      t += *ip++;
++                                              }
++                                              m_pos -= *ip++ >> 2;
++                                              m_pos -= *ip++ << 6;
++                                              if (m_pos == op)
++                                                      goto eof_found;
++                                              m_pos -= 0x4000;
++                                      }
++                                      if (litp == NULL)
++                                              goto copy_m;
++
++                                      nl = ip[-2] & 3;
++                                      if (t == 1 && lit == 0 && nl == 0
++                                          && ip[0] >= 16)
++                                      {
++                                              next_lit = nl;
++                                              lit += 3;
++                                              *litp = LZO_BYTE ((*litp & ~3)
++                                                                | lit);
++                                              copy3 (ip - 3, m_pos,
++                                                     op - m_pos);
++                                              o_m3_a++;
++                                      }
++                                      else if (t == 1 && lit <= 3 && nl == 0
++                                               && ip[0] < 16 && ip[0] != 0
++                                               && (lit + 3 + ip[0] < 16))
++                                      {
++                                              t = *ip++;
++                                              *litp &= ~3;
++                                              copy3 (ip - 4 + 1, m_pos,
++                                                     op - m_pos);
++                                              litp += 2;
++                                              if (lit > 0)
++                                                      memmove (litp + 1,
++                                                               litp, lit);
++                                              lit += 3 + t + 3;
++                                              *litp = LZO_BYTE (lit - 3);
++
++                                              o_m3_b++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              goto copy_literal_run;
++                                      }
++                              }
++                            copy_m:
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos++;
++                              do
++                                      *op++ = *m_pos++;
++                              while (--t > 0);
++                      }
++
++                    match_done:
++                      if (next_lit == NO_LIT)
++                      {
++                              t = ip[-2] & 3;
++                              lit = t;
++                              litp = ip - 2;
++                      }
++                      else
++                              t = next_lit;
++                      next_lit = NO_LIT;
++                      if (t == 0)
++                              break;
++                    match_next:
++                      do
++                              *op++ = *ip++;
++                      while (--t > 0);
++                      t = *ip++;
++              }
++      }
++
++      *out_len = op - out;
++      return LZO_E_EOF_NOT_FOUND;
++
++      eof_found:
++      *out_len = op - out;
++      return (ip == ip_end ? LZO_E_OK :
++              (ip <
++               ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++}
++
++/* interface to jffs2 follows */
++
++#include "compr.h"
++#include <linux/jffs2.h>
++
++/*#define BLOCKSIZE           JFFS2_PAGE_SIZE
++#define OUTBLOCKSIZE  (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
++
++int jffs2_lzo_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model);
++
++int jffs2_lzo_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model);
++
++static struct jffs2_compressor jffs2_lzo_comp = {
++      .priority = JFFS2_LZO_PRIORITY,
++      .name = "lzo",
++      .compr = JFFS2_COMPR_LZO,
++      .compress = &jffs2_lzo_compress,
++      .decompress = &jffs2_lzo_decompress,
++#ifdef JFFS2_LZO_DISABLED
++      .disabled = 1,
++#else
++      .disabled = 0,
++#endif
++};
++
++#ifdef JFFS2_LZO_1
++static int
++no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
++                 lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
++{
++      return 0;
++}
++#endif
++
++static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
++static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
++#ifdef JFFS2_LZO_1
++static int lzo1x_compressor_type = 999;
++static int lzo1x_optimize_type = 1;
++#endif
++static unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
++
++static lzo_bytep wrkmem = NULL;               /* temporary buffer for compression, used by lzo       */
++static lzo_bytep cmprssmem = NULL;    /* temporary buffer for compression, used by interface */
++static int cmprssmem_size = 0;
++
++static int prepare_out_buf(uint32_t ssize, uint32_t dsize)
++{
++        uint32_t msize,req;
++
++        msize = (ssize>dsize)? ssize : dsize;
++        req   = (msize<<1) + 20;
++        if ((!cmprssmem)||(cmprssmem_size<req)) {
++                if (!cmprssmem) {
++                        vfree(cmprssmem);
++                        cmprssmem = NULL;
++                        cmprssmem_size = 0;
++                }
++                cmprssmem = vmalloc(req);
++                if (!cmprssmem) {
++                        return -1;
++                }
++                cmprssmem_size = req;
++        }
++        return 0;
++}
++
++int jffs2_lzo_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model)
++{
++      lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
++      lzo_uint isize = *sourcelen;
++      int retval;
++
++        if (prepare_out_buf(*sourcelen,*dstlen)) {
++                return -1;
++        }
++      if ((retval =
++           lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
++                             wrkmem)) != LZO_E_OK)
++      {
++              return retval;
++      }
++      else
++      {
++              retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
++                                        NULL);
++              if (csize <= *dstlen) {
++                      *dstlen = csize;
++                      memcpy (output, cmprssmem, csize);
++                      return retval;
++              } else {
++                      return -1;
++              }
++      }
++}
++
++int jffs2_lzo_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model)
++{
++      lzo_uint outlen = dstlen;
++      return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
++}
++
++int jffs2_lzo_init (void)
++{
++        wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
++        if (!wrkmem) return -1;
++        jffs2_register_compressor(&jffs2_lzo_comp);
++        return 0;
++}
++
++void jffs2_lzo_exit (void)
++{
++      jffs2_unregister_compressor (&jffs2_lzo_comp);
++      if (cmprssmem) vfree(cmprssmem);
++        vfree(wrkmem);
++}
+--- linux-2.4.21/fs/jffs2/compr_rtime.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rtime.c
+@@ -1,43 +1,19 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+  * Created by Arjan van de Ven <arjanv@redhat.com>
+  *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  *
+  * Very simple lz77-ish encoder.
+  *
+  * Theory of operation: Both encoder and decoder have a list of "last
+- * occurances" for every possible source-value; after sending the
++ * occurrences" for every possible source-value; after sending the
+  * first source-byte, the second byte indicated the "run" length of
+  * matches
+  *
+@@ -49,12 +25,14 @@
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/string.h> 
++#include <linux/jffs2.h> 
++#include "compr.h"
+ /* _compress returns the compressed size, -1 if bigger */
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t *sourcelen, uint32_t *dstlen, void *model)
+ {
+-      int positions[256];
++      short positions[256];
+       int outpos = 0;
+       int pos=0;
+@@ -91,10 +69,10 @@
+ }                
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+-                    __u32 srclen, __u32 destlen)
++int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
++                    uint32_t srclen, uint32_t destlen, void *model)
+ {
+-      int positions[256];
++      short positions[256];
+       int outpos = 0;
+       int pos=0;
+       
+@@ -123,6 +101,28 @@
+                       }
+               }
+       }               
++        return 0;
+ }                
++static struct jffs2_compressor jffs2_rtime_comp = {
++    .priority = JFFS2_RTIME_PRIORITY,
++    .name = "rtime",
++    .compr = JFFS2_COMPR_RTIME,
++    .compress = &jffs2_rtime_compress,
++    .decompress = &jffs2_rtime_decompress,
++#ifdef JFFS2_RTIME_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
++
++int jffs2_rtime_init(void)
++{
++    return jffs2_register_compressor(&jffs2_rtime_comp);
++}
++void jffs2_rtime_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_rtime_comp);
++}
+--- linux-2.4.21/fs/jffs2/compr_rubin.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.c
+@@ -1,49 +1,25 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+  *
+  * Created by Arjan van de Ven <arjanv@redhat.com>
+  *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+  
+ #include <linux/string.h>
+ #include <linux/types.h>
++#include <linux/jffs2.h>
+ #include "compr_rubin.h"
+ #include "histo_mips.h"
++#include "compr.h"
+-
+-
+-void init_rubin(struct rubin_state *rs, int div, int *bits)
++static void init_rubin(struct rubin_state *rs, int div, int *bits)
+ {     
+       int c;
+@@ -56,7 +32,7 @@
+ }
+-int encode(struct rubin_state *rs, long A, long B, int symbol)
++static int encode(struct rubin_state *rs, long A, long B, int symbol)
+ {
+       long i0, i1;
+@@ -91,7 +67,7 @@
+ }
+-void end_rubin(struct rubin_state *rs)
++static void end_rubin(struct rubin_state *rs)
+ {                             
+       int i;
+@@ -104,7 +80,7 @@
+ }
+-void init_decode(struct rubin_state *rs, int div, int *bits)
++static void init_decode(struct rubin_state *rs, int div, int *bits)
+ {
+       init_rubin(rs, div, bits);              
+@@ -151,7 +127,7 @@
+       rs->rec_q = rec_q;
+ }
+-int decode(struct rubin_state *rs, long A, long B)
++static int decode(struct rubin_state *rs, long A, long B)
+ {
+       unsigned long p = rs->p, q = rs->q;
+       long i0, threshold;
+@@ -212,8 +188,8 @@
+-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, 
+-                    unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
++static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, 
++                    unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
+       {
+       int outpos = 0;
+       int pos=0;
+@@ -246,20 +222,20 @@
+ }                
+ #if 0
+ /* _compress returns the compressed size, -1 if bigger */
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t *sourcelen, uint32_t *dstlen, void *model)
+ {
+       return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ }
+ #endif
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 *sourcelen, __u32 *dstlen)
++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t *sourcelen, uint32_t *dstlen, void *model)
+ {
+       int bits[8];
+       unsigned char histo[256];
+       int i;
+       int ret;
+-      __u32 mysrclen, mydstlen;
++      uint32_t mysrclen, mydstlen;
+       mysrclen = *sourcelen;
+       mydstlen = *dstlen - 8;
+@@ -315,8 +291,8 @@
+       return 0;
+ }
+-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, 
+-                       unsigned char *page_out, __u32 srclen, __u32 destlen)
++static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, 
++                       unsigned char *page_out, uint32_t srclen, uint32_t destlen)
+ {
+       int outpos = 0;
+       struct rubin_state rs;
+@@ -330,14 +306,15 @@
+ }                
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 sourcelen, __u32 dstlen)
++int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+       rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
++        return 0;
+ }
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 sourcelen, __u32 dstlen)
++int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+       int bits[8];
+       int c;
+@@ -346,4 +323,51 @@
+               bits[c] = data_in[c];
+       rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
++        return 0;
++}
++
++static struct jffs2_compressor jffs2_rubinmips_comp = {
++    .priority = JFFS2_RUBINMIPS_PRIORITY,
++    .name = "rubinmips",
++    .compr = JFFS2_COMPR_DYNRUBIN,
++    .compress = NULL, /*&jffs2_rubinmips_compress,*/
++    .decompress = &jffs2_rubinmips_decompress,
++#ifdef JFFS2_RUBINMIPS_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
++
++int jffs2_rubinmips_init(void)
++{
++    return jffs2_register_compressor(&jffs2_rubinmips_comp);
++}
++
++void jffs2_rubinmips_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
++}
++
++static struct jffs2_compressor jffs2_dynrubin_comp = {
++    .priority = JFFS2_DYNRUBIN_PRIORITY,
++    .name = "dynrubin",
++    .compr = JFFS2_COMPR_RUBINMIPS,
++    .compress = jffs2_dynrubin_compress,
++    .decompress = &jffs2_dynrubin_decompress,
++#ifdef JFFS2_DYNRUBIN_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
++
++int jffs2_dynrubin_init(void)
++{
++    return jffs2_register_compressor(&jffs2_dynrubin_comp);
++}
++
++void jffs2_dynrubin_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
+ }
+--- linux-2.4.21/fs/jffs2/compr_rubin.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.h
+@@ -1,7 +1,7 @@
+ /* Rubin encoder/decoder header       */
+ /* work started at   : aug   3, 1994  */
+ /* last modification : aug  15, 1994  */
+-/* $Id$ */
++/* $Id$ */
+ #include "pushpull.h"
+@@ -19,10 +19,3 @@
+       int bit_divider;
+       int bits[8];
+ };
+-
+-
+-void init_rubin (struct rubin_state *rs, int div, int *bits);
+-int encode (struct rubin_state *, long, long, int);
+-void end_rubin (struct rubin_state *);
+-void init_decode (struct rubin_state *, int div, int *bits);
+-int decode (struct rubin_state *, long, long);
+--- linux-2.4.21/fs/jffs2/compr_zlib.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_zlib.c
+@@ -1,51 +1,28 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+-#ifndef __KERNEL__
++#if !defined(__KERNEL__) && !defined(__ECOS)
+ #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+ #endif
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/zlib.h>
++#include <linux/zutil.h>
++#include <asm/semaphore.h>
+ #include "nodelist.h"
++#include "compr.h"
+       /* Plan: call deflate() with avail_in == *sourcelen, 
+               avail_out = *dstlen - 12 and flush == Z_FINISH. 
+@@ -58,120 +35,184 @@
+ static DECLARE_MUTEX(deflate_sem);
+ static DECLARE_MUTEX(inflate_sem);
+-static void *deflate_workspace;
+-static void *inflate_workspace;
++static z_stream inf_strm, def_strm;
+-int __init jffs2_zlib_init(void)
++#ifdef __KERNEL__ /* Linux-only */
++#include <linux/vmalloc.h>
++#include <linux/init.h>
++
++static int __init alloc_workspaces(void)
+ {
+-      deflate_workspace = vmalloc(zlib_deflate_workspacesize());
+-      if (!deflate_workspace) {
++      def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
++      if (!def_strm.workspace) {
+               printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
+               return -ENOMEM;
+       }
+       D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+-      inflate_workspace = vmalloc(zlib_inflate_workspacesize());
+-      if (!inflate_workspace) {
++      inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
++      if (!inf_strm.workspace) {
+               printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
+-              vfree(deflate_workspace);
++              vfree(def_strm.workspace);
+               return -ENOMEM;
+       }
+       D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+       return 0;
+ }
+-void jffs2_zlib_exit(void)
++static void free_workspaces(void)
+ {
+-      vfree(deflate_workspace);
+-      vfree(inflate_workspace);
++      vfree(def_strm.workspace);
++      vfree(inf_strm.workspace);
+ }
++#else
++#define alloc_workspaces() (0)
++#define free_workspaces() do { } while(0)
++#endif /* __KERNEL__ */
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 *sourcelen, __u32 *dstlen)
++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t *sourcelen, uint32_t *dstlen, void *model)
+ {
+-      z_stream strm;
+       int ret;
+       if (*dstlen <= STREAM_END_SPACE)
+               return -1;
+       down(&deflate_sem);
+-      strm.workspace = deflate_workspace;
+-      if (Z_OK != zlib_deflateInit(&strm, 3)) {
++      if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
+               printk(KERN_WARNING "deflateInit failed\n");
+               up(&deflate_sem);
+               return -1;
+       }
+-      strm.next_in = data_in;
+-      strm.total_in = 0;
++      def_strm.next_in = data_in;
++      def_strm.total_in = 0;
+       
+-      strm.next_out = cpage_out;
+-      strm.total_out = 0;
++      def_strm.next_out = cpage_out;
++      def_strm.total_out = 0;
+-      while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+-              strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+-              strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
++      while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
++              def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
++              def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
+               D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
+-                        strm.avail_in, strm.avail_out));
+-              ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
++                        def_strm.avail_in, def_strm.avail_out));
++              ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
+               D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", 
+-                        strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
++                        def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
+               if (ret != Z_OK) {
+                       D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
+-                      zlib_deflateEnd(&strm);
++                      zlib_deflateEnd(&def_strm);
+                       up(&deflate_sem);
+                       return -1;
+               }
+       }
+-      strm.avail_out += STREAM_END_SPACE;
+-      strm.avail_in = 0;
+-      ret = zlib_deflate(&strm, Z_FINISH);
+-      zlib_deflateEnd(&strm);
+-      up(&deflate_sem);
++      def_strm.avail_out += STREAM_END_SPACE;
++      def_strm.avail_in = 0;
++      ret = zlib_deflate(&def_strm, Z_FINISH);
++      zlib_deflateEnd(&def_strm);
++
+       if (ret != Z_STREAM_END) {
+               D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
+-              return -1;
++              ret = -1;
++              goto out;
+       }
+-      D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+-                strm.total_in, strm.total_out));
++      if (def_strm.total_out >= def_strm.total_in) {
++              D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
++                        def_strm.total_in, def_strm.total_out));
++              ret = -1;
++              goto out;
++      }
+-      if (strm.total_out >= strm.total_in)
+-              return -1;
++      D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
++                def_strm.total_in, def_strm.total_out));
+-      *dstlen = strm.total_out;
+-      *sourcelen = strm.total_in;
+-      return 0;
++      *dstlen = def_strm.total_out;
++      *sourcelen = def_strm.total_in;
++      ret = 0;
++ out:
++      up(&deflate_sem);
++      return ret;
+ }
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+-                    __u32 srclen, __u32 destlen)
++int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
++                    uint32_t srclen, uint32_t destlen, void *model)
+ {
+-      z_stream strm;
+       int ret;
++      int wbits = MAX_WBITS;
+       down(&inflate_sem);
+-      strm.workspace = inflate_workspace;
+-      if (Z_OK != zlib_inflateInit(&strm)) {
++      inf_strm.next_in = data_in;
++      inf_strm.avail_in = srclen;
++      inf_strm.total_in = 0;
++      
++      inf_strm.next_out = cpage_out;
++      inf_strm.avail_out = destlen;
++      inf_strm.total_out = 0;
++
++      /* If it's deflate, and it's got no preset dictionary, then
++         we can tell zlib to skip the adler32 check. */
++      if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
++          ((data_in[0] & 0x0f) == Z_DEFLATED) &&
++          !(((data_in[0]<<8) + data_in[1]) % 31)) {
++
++              D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
++              wbits = -((data_in[0] >> 4) + 8);
++              inf_strm.next_in += 2;
++              inf_strm.avail_in -= 2;
++      } else {
++              /* Let this remain D1 for now -- it should never happen */
++              D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
++      }
++
++
++      if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+               printk(KERN_WARNING "inflateInit failed\n");
+               up(&inflate_sem);
+-              return;
++              return 1;
+       }
+-      strm.next_in = data_in;
+-      strm.avail_in = srclen;
+-      strm.total_in = 0;
+-      
+-      strm.next_out = cpage_out;
+-      strm.avail_out = destlen;
+-      strm.total_out = 0;
+-      while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
++      while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
+               ;
+       if (ret != Z_STREAM_END) {
+               printk(KERN_NOTICE "inflate returned %d\n", ret);
+       }
+-      zlib_inflateEnd(&strm);
++      zlib_inflateEnd(&inf_strm);
+       up(&inflate_sem);
++        return 0;
++}
++
++static struct jffs2_compressor jffs2_zlib_comp = {
++    .priority = JFFS2_ZLIB_PRIORITY,
++    .name = "zlib",
++    .compr = JFFS2_COMPR_ZLIB,
++    .compress = &jffs2_zlib_compress,
++    .decompress = &jffs2_zlib_decompress,
++#ifdef JFFS2_ZLIB_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
++
++int __init jffs2_zlib_init(void)
++{
++    int ret;
++
++    ret = alloc_workspaces();
++    if (ret)
++        return ret;
++
++    ret = jffs2_register_compressor(&jffs2_zlib_comp);
++    if (ret)
++        free_workspaces();
++
++    return ret;
++}
++
++void jffs2_zlib_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_zlib_comp);
++    free_workspaces();
+ }
+--- linux-2.4.21/fs/jffs2/crc32.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.c
+@@ -37,11 +37,11 @@
+  *      polynomial $edb88320
+  */
+-/* $Id$ */
++/* $Id$ */
+ #include "crc32.h"
+-const __u32 crc32_table[256] = {
++const uint32_t crc32_table[256] = {
+       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+--- linux-2.4.21/fs/jffs2/crc32.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.h
+@@ -1,16 +1,16 @@
+ #ifndef CRC32_H
+ #define CRC32_H
+-/* $Id$ */
++/* $Id$ */
+ #include <linux/types.h>
+-extern const __u32 crc32_table[256];
++extern const uint32_t crc32_table[256];
+ /* Return a 32-bit CRC of the contents of the buffer. */
+-static inline __u32 
+-crc32(__u32 val, const void *ss, int len)
++static inline uint32_t 
++crc32(uint32_t val, const void *ss, int len)
+ {
+       const unsigned char *s = ss;
+         while (--len >= 0)
+--- linux-2.4.21/fs/jffs2/dir.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/dir.c
+@@ -1,84 +1,73 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+-#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/time.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++
++/* Urgh. Please tell me there's a nicer way of doing these. */
++#include <linux/version.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++typedef int mknod_arg_t;
++#define NAMEI_COMPAT(x) ((void *)x)
++#else
++typedef dev_t mknod_arg_t;
++#define NAMEI_COMPAT(x) (x)
++#endif
+ static int jffs2_readdir (struct file *, void *, filldir_t);
+-static int jffs2_create (struct inode *,struct dentry *,int);
+-static struct dentry *jffs2_lookup (struct inode *,struct dentry *);
++static int jffs2_create (struct inode *,struct dentry *,int,
++                       struct nameidata *);
++static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
++                                  struct nameidata *);
+ static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
+ static int jffs2_unlink (struct inode *,struct dentry *);
+ static int jffs2_symlink (struct inode *,struct dentry *,const char *);
+ static int jffs2_mkdir (struct inode *,struct dentry *,int);
+ static int jffs2_rmdir (struct inode *,struct dentry *);
+-static int jffs2_mknod (struct inode *,struct dentry *,int,int);
++static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
+ static int jffs2_rename (struct inode *, struct dentry *,
+                         struct inode *, struct dentry *);
+ struct file_operations jffs2_dir_operations =
+ {
+-      read:           generic_read_dir,
+-      readdir:        jffs2_readdir,
+-      ioctl:          jffs2_ioctl,
+-      fsync:          jffs2_null_fsync
++      .read =         generic_read_dir,
++      .readdir =      jffs2_readdir,
++      .ioctl =        jffs2_ioctl,
++      .fsync =        jffs2_fsync
+ };
+ struct inode_operations jffs2_dir_inode_operations =
+ {
+-      create:         jffs2_create,
+-      lookup:         jffs2_lookup,
+-      link:           jffs2_link,
+-      unlink:         jffs2_unlink,
+-      symlink:        jffs2_symlink,
+-      mkdir:          jffs2_mkdir,
+-      rmdir:          jffs2_rmdir,
+-      mknod:          jffs2_mknod,
+-      rename:         jffs2_rename,
+-      setattr:        jffs2_setattr,
++      .create =       NAMEI_COMPAT(jffs2_create),
++      .lookup =       NAMEI_COMPAT(jffs2_lookup),
++      .link =         jffs2_link,
++      .unlink =       jffs2_unlink,
++      .symlink =      jffs2_symlink,
++      .mkdir =        jffs2_mkdir,
++      .rmdir =        jffs2_rmdir,
++      .mknod =        jffs2_mknod,
++      .rename =       jffs2_rename,
++      .setattr =      jffs2_setattr,
+ };
+ /***********************************************************************/
+@@ -88,12 +77,13 @@
+    and we use the same hash function as the dentries. Makes this 
+    nice and simple
+ */
+-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
++static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
++                                 struct nameidata *nd)
+ {
+       struct jffs2_inode_info *dir_f;
+       struct jffs2_sb_info *c;
+       struct jffs2_full_dirent *fd = NULL, *fd_list;
+-      __u32 ino = 0;
++      uint32_t ino = 0;
+       struct inode *inode = NULL;
+       D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+@@ -153,8 +143,9 @@
+               offset++;
+       }
+       if (offset == 1) {
+-              D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino));
+-              if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
++              unsigned long pino = parent_ino(filp->f_dentry);
++              D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
++              if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
+                       goto out;
+               offset++;
+       }
+@@ -188,18 +179,14 @@
+ /***********************************************************************/
+-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
++
++static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
++                      struct nameidata *nd)
+ {
++      struct jffs2_raw_inode *ri;
+       struct jffs2_inode_info *f, *dir_f;
+       struct jffs2_sb_info *c;
+       struct inode *inode;
+-      struct jffs2_raw_inode *ri;
+-      struct jffs2_raw_dirent *rd;
+-      struct jffs2_full_dnode *fn;
+-      struct jffs2_full_dirent *fd;
+-      int namelen;
+-      __u32 alloclen, phys_ofs;
+-      __u32 writtenlen;
+       int ret;
+       ri = jffs2_alloc_raw_inode();
+@@ -210,23 +197,11 @@
+       D1(printk(KERN_DEBUG "jffs2_create()\n"));
+-      /* Try to reserve enough space for both node and dirent. 
+-       * Just the node will do for now, though 
+-       */
+-      namelen = dentry->d_name.len;
+-      ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+-      D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
+-      if (ret) {
+-              jffs2_free_raw_inode(ri);
+-              return ret;
+-      }
+-
+       inode = jffs2_new_inode(dir_i, mode, ri);
+       if (IS_ERR(inode)) {
+               D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
+               jffs2_free_raw_inode(ri);
+-              jffs2_complete_reservation(c);
+               return PTR_ERR(inode);
+       }
+@@ -236,93 +211,21 @@
+       inode->i_mapping->nrpages = 0;
+       f = JFFS2_INODE_INFO(inode);
++      dir_f = JFFS2_INODE_INFO(dir_i);
+-      ri->data_crc = 0;
+-      ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+-
+-      fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+-      D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
+-      jffs2_free_raw_inode(ri);
+-
+-      if (IS_ERR(fn)) {
+-              D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+-              /* Eeek. Wave bye bye */
+-              up(&f->sem);
+-              jffs2_complete_reservation(c);
+-              jffs2_clear_inode(inode);
+-              return PTR_ERR(fn);
+-      }
+-      /* No data here. Only a metadata node, which will be 
+-         obsoleted by the first data write
+-      */
+-      f->metadata = fn;
+-
+-      /* Work out where to put the dirent node now. */
+-      writtenlen = PAD(writtenlen);
+-      phys_ofs += writtenlen;
+-      alloclen -= writtenlen;
+-      up(&f->sem);
+-
+-      if (alloclen < sizeof(*rd)+namelen) {
+-              /* Not enough space left in this chunk. Get some more */
+-              jffs2_complete_reservation(c);
+-              ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++      ret = jffs2_do_create(c, dir_f, f, ri, 
++                            dentry->d_name.name, dentry->d_name.len);
+               
+               if (ret) {
+-                      /* Eep. */
+-                      D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+-                      jffs2_clear_inode(inode);
++              make_bad_inode(inode);
++              iput(inode);
++              jffs2_free_raw_inode(ri);
+                       return ret;
+               }
+-      }
+-
+-      rd = jffs2_alloc_raw_dirent();
+-      if (!rd) {
+-              /* Argh. Now we treat it like a normal delete */
+-              jffs2_complete_reservation(c);
+-              jffs2_clear_inode(inode);
+-              return -ENOMEM;
+-      }
+-
+-      dir_f = JFFS2_INODE_INFO(dir_i);
+-      down(&dir_f->sem);
+-
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + namelen;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
+-      rd->nsize = namelen;
+-      rd->type = DT_REG;
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+-      jffs2_complete_reservation(c);
+-      
+-      if (IS_ERR(fd)) {
+-              /* dirent failed to write. Delete the inode normally 
+-                 as if it were the final unlink() */
+-              jffs2_free_raw_dirent(rd);
+-              up(&dir_f->sem);
+-              jffs2_clear_inode(inode);
+-              return PTR_ERR(fd);
+-      }
+-
+-      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+-
+-      jffs2_free_raw_dirent(rd);
+-
+-      /* Link the fd into the inode's list, obsoleting an old
+-         one if necessary. */
+-      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+-      up(&dir_f->sem);
++      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
++      jffs2_free_raw_inode(ri);
+       d_instantiate(dentry, inode);
+       D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
+@@ -332,173 +235,48 @@
+ /***********************************************************************/
+-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+-      struct jffs2_inode_info *dir_f, *f;
+-      struct jffs2_sb_info *c;
+-      struct jffs2_raw_dirent *rd;
+-      struct jffs2_full_dirent *fd;
+-      __u32 alloclen, phys_ofs;
+-      int ret;
+-
+-      c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+-      rd = jffs2_alloc_raw_dirent();
+-      if (!rd)
+-              return -ENOMEM;
+-
+-      ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+-      if (ret) {
+-              jffs2_free_raw_dirent(rd);
+-              return ret;
+-      }
+-
+-      dir_f = JFFS2_INODE_INFO(dir_i);
+-      down(&dir_f->sem);
+-
+-      /* Build a deletion node */
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + dentry->d_name.len;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = 0;
+-      rd->mctime = CURRENT_TIME;
+-      rd->nsize = dentry->d_name.len;
+-      rd->type = DT_UNKNOWN;
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-      
+-      jffs2_complete_reservation(c);
+-      jffs2_free_raw_dirent(rd);
+-
+-      if (IS_ERR(fd)) {
+-              up(&dir_f->sem);
+-              return PTR_ERR(fd);
+-      }
+-
+-      /* File it. This will mark the old one obsolete. */
+-      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+-      up(&dir_f->sem);
+-      
+-      if (!rename) {
+-              f = JFFS2_INODE_INFO(dentry->d_inode);
+-              down(&f->sem);
+-
+-              while (f->dents) {
+-                      /* There can be only deleted ones */
+-                      fd = f->dents;
+-                      
+-                      f->dents = fd->next;
+-                      
+-                      if (fd->ino) {
+-                              printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+-                                     f->inocache->ino, fd->name, fd->ino);
+-                      } else {
+-                              D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
+-                      }
+-                      jffs2_mark_node_obsolete(c, fd->raw);
+-                      jffs2_free_full_dirent(fd);
+-              }
+-              /* Don't oops on unlinking a bad inode */
+-              if (f->inocache)
+-                      f->inocache->nlink--;
+-              dentry->d_inode->i_nlink--;
+-              up(&f->sem);
+-      }
+-
+-      return 0;
+-}
+ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
+ {
+-      return jffs2_do_unlink(dir_i, dentry, 0);
+-}
+-/***********************************************************************/
+-
+-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+-      struct jffs2_inode_info *dir_f, *f;
+-      struct jffs2_sb_info *c;
+-      struct jffs2_raw_dirent *rd;
+-      struct jffs2_full_dirent *fd;
+-      __u32 alloclen, phys_ofs;
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
++      struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
++      struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode);
+       int ret;
+-      c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+-      rd = jffs2_alloc_raw_dirent();
+-      if (!rd)
+-              return -ENOMEM;
+-
+-      ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
+-      if (ret) {
+-              jffs2_free_raw_dirent(rd);
++      ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, 
++                             dentry->d_name.len, dead_f);
++      if (dead_f->inocache)
++              dentry->d_inode->i_nlink = dead_f->inocache->nlink;
+               return ret;
+-      }
+-      
+-      dir_f = JFFS2_INODE_INFO(dir_i);
+-      down(&dir_f->sem);
+-
+-      /* Build a deletion node */
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + dentry->d_name.len;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = old_dentry->d_inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
+-      rd->nsize = dentry->d_name.len;
+-
+-      /* XXX: This is ugly. */
+-      rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+-      if (!rd->type) rd->type = DT_REG;
+-
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-      
+-      jffs2_complete_reservation(c);
+-      jffs2_free_raw_dirent(rd);
+-
+-      if (IS_ERR(fd)) {
+-              up(&dir_f->sem);
+-              return PTR_ERR(fd);
+-      }
+-
+-      /* File it. This will mark the old one obsolete. */
+-      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+-      up(&dir_f->sem);
+-
+-      if (!rename) {
+-              f = JFFS2_INODE_INFO(old_dentry->d_inode);
+-              down(&f->sem);
+-              old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+-              up(&f->sem);
+-      }
+-      return 0;
+ }
++/***********************************************************************/
++
+ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
+ {
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb);
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
++      struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
+       int ret;
++      uint8_t type;
+-      /* Can't link a bad inode. */
+-      if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache)
++      /* Don't let people make hard links to bad inodes. */
++      if (!f->inocache)
+               return -EIO;
+       if (S_ISDIR(old_dentry->d_inode->i_mode))
+               return -EPERM;
+-      ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
++      /* XXX: This is ugly */
++      type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++      if (!type) type = DT_REG;
++
++      ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
++
+       if (!ret) {
++              down(&f->sem);
++              old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
++              up(&f->sem);
+               d_instantiate(dentry, old_dentry->d_inode);
+               atomic_inc(&old_dentry->d_inode->i_count);
+       }
+@@ -517,13 +295,12 @@
+       struct jffs2_full_dnode *fn;
+       struct jffs2_full_dirent *fd;
+       int namelen;
+-      __u32 alloclen, phys_ofs;
+-      __u32 writtenlen;
+-      int ret;
++      uint32_t alloclen, phys_ofs;
++      int ret, targetlen = strlen(target);
+       /* FIXME: If you care. We'd need to use frags for the target
+          if it grows much more than this */
+-      if (strlen(target) > 254)
++      if (targetlen > 254)
+               return -EINVAL;
+       ri = jffs2_alloc_raw_inode();
+@@ -537,7 +314,7 @@
+        * Just the node will do for now, though 
+        */
+       namelen = dentry->d_name.len;
+-      ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
++      ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       if (ret) {
+               jffs2_free_raw_inode(ri);
+@@ -556,15 +333,16 @@
+       f = JFFS2_INODE_INFO(inode);
+-      inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
+-      ri->totlen = sizeof(*ri) + ri->dsize;
+-      ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++      inode->i_size = targetlen;
++      ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
++      ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
++      ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+       ri->compr = JFFS2_COMPR_NONE;
+-      ri->data_crc = crc32(0, target, strlen(target));
+-      ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++      ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
++      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+       
+-      fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
++      fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
+       jffs2_free_raw_inode(ri);
+@@ -575,19 +353,26 @@
+               jffs2_clear_inode(inode);
+               return PTR_ERR(fn);
+       }
++
++      /* We use f->dents field to store the target path. */
++      f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
++      if (!f->dents) {
++              printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
++              up(&f->sem);
++              jffs2_complete_reservation(c);
++              jffs2_clear_inode(inode);
++              return -ENOMEM;
++      }
++
++      memcpy(f->dents, target, targetlen + 1);
++      D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
++
+       /* No data here. Only a metadata node, which will be 
+          obsoleted by the first data write
+       */
+       f->metadata = fn;
+       up(&f->sem);
+-      /* Work out where to put the dirent node now. */
+-      writtenlen = (writtenlen+3)&~3;
+-      phys_ofs += writtenlen;
+-      alloclen -= writtenlen;
+-
+-      if (alloclen < sizeof(*rd)+namelen) {
+-              /* Not enough space left in this chunk. Get some more */
+               jffs2_complete_reservation(c);
+               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+               if (ret) {
+@@ -595,7 +380,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -608,41 +392,42 @@
+       dir_f = JFFS2_INODE_INFO(dir_i);
+       down(&dir_f->sem);
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + namelen;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
++      rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++      rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++      rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      rd->pino = cpu_to_je32(dir_i->i_ino);
++      rd->version = cpu_to_je32(++dir_f->highest_version);
++      rd->ino = cpu_to_je32(inode->i_ino);
++      rd->mctime = cpu_to_je32(get_seconds());
+       rd->nsize = namelen;
+       rd->type = DT_LNK;
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
++      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+       
+-      jffs2_complete_reservation(c);
++      fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+       
+       if (IS_ERR(fd)) {
+               /* dirent failed to write. Delete the inode normally 
+                  as if it were the final unlink() */
++              jffs2_complete_reservation(c);
+               jffs2_free_raw_dirent(rd);
+               up(&dir_f->sem);
+               jffs2_clear_inode(inode);
+               return PTR_ERR(fd);
+       }
+-      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+       jffs2_free_raw_dirent(rd);
+       /* Link the fd into the inode's list, obsoleting an old
+          one if necessary. */
+       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+       up(&dir_f->sem);
++      jffs2_complete_reservation(c);
+       d_instantiate(dentry, inode);
+       return 0;
+@@ -659,8 +444,7 @@
+       struct jffs2_full_dnode *fn;
+       struct jffs2_full_dirent *fd;
+       int namelen;
+-      __u32 alloclen, phys_ofs;
+-      __u32 writtenlen;
++      uint32_t alloclen, phys_ofs;
+       int ret;
+       mode |= S_IFDIR;
+@@ -692,13 +476,15 @@
+       inode->i_op = &jffs2_dir_inode_operations;
+       inode->i_fop = &jffs2_dir_operations;
++      /* Directories get nlink 2 at start */
++      inode->i_nlink = 2;
+       f = JFFS2_INODE_INFO(inode);
+-      ri->data_crc = 0;
+-      ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++      ri->data_crc = cpu_to_je32(0);
++      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+       
+-      fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
++      fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+       jffs2_free_raw_inode(ri);
+@@ -715,13 +501,6 @@
+       f->metadata = fn;
+       up(&f->sem);
+-      /* Work out where to put the dirent node now. */
+-      writtenlen = PAD(writtenlen);
+-      phys_ofs += writtenlen;
+-      alloclen -= writtenlen;
+-
+-      if (alloclen < sizeof(*rd)+namelen) {
+-              /* Not enough space left in this chunk. Get some more */
+               jffs2_complete_reservation(c);
+               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+               if (ret) {
+@@ -729,7 +508,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
+       
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -742,41 +520,43 @@
+       dir_f = JFFS2_INODE_INFO(dir_i);
+       down(&dir_f->sem);
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + namelen;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
++      rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++      rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++      rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      rd->pino = cpu_to_je32(dir_i->i_ino);
++      rd->version = cpu_to_je32(++dir_f->highest_version);
++      rd->ino = cpu_to_je32(inode->i_ino);
++      rd->mctime = cpu_to_je32(get_seconds());
+       rd->nsize = namelen;
+       rd->type = DT_DIR;
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
++      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+       
+-      jffs2_complete_reservation(c);
++      fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+       
+       if (IS_ERR(fd)) {
+               /* dirent failed to write. Delete the inode normally 
+                  as if it were the final unlink() */
++              jffs2_complete_reservation(c);
+               jffs2_free_raw_dirent(rd);
+               up(&dir_f->sem);
+               jffs2_clear_inode(inode);
+               return PTR_ERR(fd);
+       }
+-      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
++      dir_i->i_nlink++;
+       jffs2_free_raw_dirent(rd);
+       /* Link the fd into the inode's list, obsoleting an old
+          one if necessary. */
+       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+       up(&dir_f->sem);
++      jffs2_complete_reservation(c);
+       d_instantiate(dentry, inode);
+       return 0;
+@@ -786,15 +566,19 @@
+ {
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+       struct jffs2_full_dirent *fd;
++      int ret;
+       for (fd = f->dents ; fd; fd = fd->next) {
+               if (fd->ino)
+                       return -ENOTEMPTY;
+       }
+-      return jffs2_unlink(dir_i, dentry);
++      ret = jffs2_unlink(dir_i, dentry);
++      if (!ret)
++              dir_i->i_nlink--;
++      return ret;
+ }
+-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
+ {
+       struct jffs2_inode_info *f, *dir_f;
+       struct jffs2_sb_info *c;
+@@ -804,12 +588,14 @@
+       struct jffs2_full_dnode *fn;
+       struct jffs2_full_dirent *fd;
+       int namelen;
+-      unsigned short dev;
++      jint16_t dev;
+       int devlen = 0;
+-      __u32 alloclen, phys_ofs;
+-      __u32 writtenlen;
++      uint32_t alloclen, phys_ofs;
+       int ret;
++      if (!old_valid_dev(rdev))
++              return -EINVAL;
++
+       ri = jffs2_alloc_raw_inode();
+       if (!ri)
+               return -ENOMEM;
+@@ -817,7 +603,7 @@
+       c = JFFS2_SB_INFO(dir_i->i_sb);
+       
+       if (S_ISBLK(mode) || S_ISCHR(mode)) {
+-              dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev));
++              dev = cpu_to_je16(old_encode_dev(rdev));
+               devlen = sizeof(dev);
+       }
+       
+@@ -844,15 +630,15 @@
+       f = JFFS2_INODE_INFO(inode);
+-      ri->dsize = ri->csize = devlen;
+-      ri->totlen = sizeof(*ri) + ri->csize;
+-      ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++      ri->dsize = ri->csize = cpu_to_je32(devlen);
++      ri->totlen = cpu_to_je32(sizeof(*ri) + devlen);
++      ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+       ri->compr = JFFS2_COMPR_NONE;
+-      ri->data_crc = crc32(0, &dev, devlen);
+-      ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++      ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
++      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+       
+-      fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
++      fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
+       jffs2_free_raw_inode(ri);
+@@ -869,13 +655,6 @@
+       f->metadata = fn;
+       up(&f->sem);
+-      /* Work out where to put the dirent node now. */
+-      writtenlen = (writtenlen+3)&~3;
+-      phys_ofs += writtenlen;
+-      alloclen -= writtenlen;
+-
+-      if (alloclen < sizeof(*rd)+namelen) {
+-              /* Not enough space left in this chunk. Get some more */
+               jffs2_complete_reservation(c);
+               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+               if (ret) {
+@@ -883,7 +662,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -896,44 +674,45 @@
+       dir_f = JFFS2_INODE_INFO(dir_i);
+       down(&dir_f->sem);
+-      rd->magic = JFFS2_MAGIC_BITMASK;
+-      rd->nodetype = JFFS2_NODETYPE_DIRENT;
+-      rd->totlen = sizeof(*rd) + namelen;
+-      rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
++      rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++      rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++      rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      rd->pino = cpu_to_je32(dir_i->i_ino);
++      rd->version = cpu_to_je32(++dir_f->highest_version);
++      rd->ino = cpu_to_je32(inode->i_ino);
++      rd->mctime = cpu_to_je32(get_seconds());
+       rd->nsize = namelen;
+       /* XXX: This is ugly. */
+       rd->type = (mode & S_IFMT) >> 12;
+-      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+-      rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+-      fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
++      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+       
+-      jffs2_complete_reservation(c);
++      fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+       
+       if (IS_ERR(fd)) {
+               /* dirent failed to write. Delete the inode normally 
+                  as if it were the final unlink() */
++              jffs2_complete_reservation(c);
+               jffs2_free_raw_dirent(rd);
+               up(&dir_f->sem);
+               jffs2_clear_inode(inode);
+               return PTR_ERR(fd);
+       }
+-      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+       jffs2_free_raw_dirent(rd);
+       /* Link the fd into the inode's list, obsoleting an old
+          one if necessary. */
+       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+       up(&dir_f->sem);
++      jffs2_complete_reservation(c);
+       d_instantiate(dentry, inode);
+@@ -944,7 +723,9 @@
+                         struct inode *new_dir_i, struct dentry *new_dentry)
+ {
+       int ret;
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
+       struct jffs2_inode_info *victim_f = NULL;
++      uint8_t type;
+       /* The VFS will check for us and prevent trying to rename a 
+        * file over a directory and vice versa, but if it's a directory,
+@@ -973,7 +754,15 @@
+       */
+       /* Make a hard link */
+-      ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
++      
++      /* XXX: This is ugly */
++      type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++      if (!type) type = DT_REG;
++
++      ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), 
++                          old_dentry->d_inode->i_ino, type,
++                          new_dentry->d_name.name, new_dentry->d_name.len);
++
+       if (ret)
+               return ret;
+@@ -989,22 +778,36 @@
+               }
+       }
++      /* If it was a directory we moved, and there was no victim, 
++         increase i_nlink on its new parent */
++      if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
++              new_dir_i->i_nlink++;
++
+       /* Unlink the original */
+-      ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
++      ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), 
++                    old_dentry->d_name.name, old_dentry->d_name.len, NULL);
++
++      /* We don't touch inode->i_nlink */
+       
+       if (ret) {
+               /* Oh shit. We really ought to make a single node which can do both atomically */
+               struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+               down(&f->sem);
++              old_dentry->d_inode->i_nlink++;
+               if (f->inocache)
+-                      old_dentry->d_inode->i_nlink = f->inocache->nlink++;
++                      f->inocache->nlink++;
+               up(&f->sem);
+                      
+               printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+               /* Might as well let the VFS know */
+               d_instantiate(new_dentry, old_dentry->d_inode);
+               atomic_inc(&old_dentry->d_inode->i_count);
+-      }
+       return ret;
++      }
++
++      if (S_ISDIR(old_dentry->d_inode->i_mode))
++              old_dir_i->i_nlink--;
++
++      return 0;
+ }
+--- linux-2.4.21/fs/jffs2/erase.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/erase.c
+@@ -1,68 +1,63 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
++
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/crc32.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
+ #include "nodelist.h"
+-#include "crc32.h"
+ struct erase_priv_struct {
+       struct jffs2_eraseblock *jeb;
+       struct jffs2_sb_info *c;
+ };
+       
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *);
++#endif
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+-      struct erase_info *instr;
+       int ret;
++      uint32_t bad_offset;
++#ifdef __ECOS
++       ret = jffs2_flash_erase(c, jeb);
++       if (!ret) {
++               jffs2_erase_succeeded(c, jeb);
++               return;
++       }
++       bad_offset = jeb->offset;
++#else /* Linux */
++      struct erase_info *instr;
++      D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
+       instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+       if (!instr) {
+               printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+-              spin_lock_bh(&c->erase_completion_lock);
++              spin_lock(&c->erase_completion_lock);
+               list_del(&jeb->list);
+               list_add(&jeb->list, &c->erase_pending_list);
+               c->erasing_size -= c->sector_size;
+-              spin_unlock_bh(&c->erase_completion_lock);
++              c->dirty_size += c->sector_size;
++              jeb->dirty_size = c->sector_size;
++              spin_unlock(&c->erase_completion_lock);
+               return;
+       }
+@@ -73,23 +68,29 @@
+       instr->len = c->sector_size;
+       instr->callback = jffs2_erase_callback;
+       instr->priv = (unsigned long)(&instr[1]);
++      instr->fail_addr = 0xffffffff;
+       
+       ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+       ((struct erase_priv_struct *)instr->priv)->c = c;
+       ret = c->mtd->erase(c->mtd, instr);
+-      if (!ret) {
++      if (!ret)
+               return;
+-      }
++
++      bad_offset = instr->fail_addr;
++      kfree(instr);
++#endif /* __ECOS */
++
+       if (ret == -ENOMEM || ret == -EAGAIN) {
+               /* Erase failed immediately. Refile it on the list */
+               D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+-              spin_lock_bh(&c->erase_completion_lock);
++              spin_lock(&c->erase_completion_lock);
+               list_del(&jeb->list);
+               list_add(&jeb->list, &c->erase_pending_list);
+               c->erasing_size -= c->sector_size;
+-              spin_unlock_bh(&c->erase_completion_lock);
+-              kfree(instr);
++              c->dirty_size += c->sector_size;
++              jeb->dirty_size = c->sector_size;
++              spin_unlock(&c->erase_completion_lock);
+               return;
+       }
+@@ -97,74 +98,119 @@
+               printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
+       else
+               printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
+-      spin_lock_bh(&c->erase_completion_lock);
+-      list_del(&jeb->list);
+-      list_add(&jeb->list, &c->bad_list);
+-      c->nr_erasing_blocks--;
+-      c->bad_size += c->sector_size;
+-      c->erasing_size -= c->sector_size;
+-      spin_unlock_bh(&c->erase_completion_lock);
+-      wake_up(&c->erase_wait);
+-      kfree(instr);
++
++      jffs2_erase_failed(c, jeb, bad_offset);
+ }
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+ {
+       struct jffs2_eraseblock *jeb;
+-      spin_lock_bh(&c->erase_completion_lock);
+-      while (!list_empty(&c->erase_pending_list)) {
++      down(&c->erase_free_sem);
+-              jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++      spin_lock(&c->erase_completion_lock);
+-              D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
++      while (!list_empty(&c->erase_complete_list) ||
++             !list_empty(&c->erase_pending_list)) {
++
++              if (!list_empty(&c->erase_complete_list)) {
++                      jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
++                      list_del(&jeb->list);
++                      spin_unlock(&c->erase_completion_lock);
++                      jffs2_mark_erased_block(c, jeb);
++
++                      if (!--count) {
++                              D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
++                              goto done;
++                      }
++              } else if (!list_empty(&c->erase_pending_list)) {
++                      jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++                      D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+               list_del(&jeb->list);
+               c->erasing_size += c->sector_size;
++                      c->wasted_size -= jeb->wasted_size;
+               c->free_size -= jeb->free_size;
+               c->used_size -= jeb->used_size;
+               c->dirty_size -= jeb->dirty_size;
+-              jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
++                      jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+               jffs2_free_all_node_refs(c, jeb);
+               list_add(&jeb->list, &c->erasing_list);
+-              spin_unlock_bh(&c->erase_completion_lock);
++                      spin_unlock(&c->erase_completion_lock);
+               
+               jffs2_erase_block(c, jeb);
++
++              } else {
++                      BUG();
++              }
++
+               /* Be nice */
+-              if (current->need_resched)
+-                      schedule();
+-              spin_lock_bh(&c->erase_completion_lock);
++              cond_resched();
++              spin_lock(&c->erase_completion_lock);
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
++
++      spin_unlock(&c->erase_completion_lock);
++ done:
+       D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
++
++      up(&c->erase_free_sem);
+ }
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++      D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
++      spin_lock(&c->erase_completion_lock);
++      list_del(&jeb->list);
++      list_add_tail(&jeb->list, &c->erase_complete_list);
++      spin_unlock(&c->erase_completion_lock);
++      /* Ensure that kupdated calls us again to mark them clean */
++      jffs2_erase_pending_trigger(c);
++}
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
++{
++      /* For NAND, if the failure did not occur at the device level for a
++         specific physical page, don't bother updating the bad block table. */
++      if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
++              /* We had a device-level failure to erase.  Let's see if we've
++                 failed too many times. */
++              if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
++                      /* We'd like to give this block another try. */
++                      spin_lock(&c->erase_completion_lock);
++                      list_del(&jeb->list);
++                      list_add(&jeb->list, &c->erase_pending_list);
++                      c->erasing_size -= c->sector_size;
++                      c->dirty_size += c->sector_size;
++                      jeb->dirty_size = c->sector_size;
++                      spin_unlock(&c->erase_completion_lock);
++                      return;
++              }
++      }
++
++      spin_lock(&c->erase_completion_lock);
++      c->erasing_size -= c->sector_size;
++      c->bad_size += c->sector_size;
++      list_del(&jeb->list);
++      list_add(&jeb->list, &c->bad_list);
++      c->nr_erasing_blocks--;
++      spin_unlock(&c->erase_completion_lock);
++      wake_up(&c->erase_wait);
++}      
++
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *instr)
+ {
+       struct erase_priv_struct *priv = (void *)instr->priv;
+       if(instr->state != MTD_ERASE_DONE) {
+               printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
+-              spin_lock(&priv->c->erase_completion_lock);
+-              priv->c->erasing_size -= priv->c->sector_size;
+-              priv->c->bad_size += priv->c->sector_size;
+-              list_del(&priv->jeb->list);
+-              list_add(&priv->jeb->list, &priv->c->bad_list);
+-              priv->c->nr_erasing_blocks--;
+-              spin_unlock(&priv->c->erase_completion_lock);
+-              wake_up(&priv->c->erase_wait);
++              jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
+       } else {
+-              D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
+-              spin_lock(&priv->c->erase_completion_lock);
+-              list_del(&priv->jeb->list);
+-              list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
+-              spin_unlock(&priv->c->erase_completion_lock);
++              jffs2_erase_succeeded(priv->c, priv->jeb);
+       }       
+-      /* Make sure someone picks up the block off the erase_complete list */
+-      OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
+       kfree(instr);
+ }
++#endif /* !__ECOS */
+ /* Hmmm. Maybe we should accept the extra space it takes and make
+    this a standard doubly-linked list? */
+@@ -187,7 +233,7 @@
+                       continue;
+               } 
+-              if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
++              if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
+                       /* It's in the block we're erasing */
+                       struct jffs2_raw_node_ref *this;
+@@ -221,7 +267,7 @@
+               this = ic->nodes;
+          
+               while(this) {
+-                      printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
++                      printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
+                       if (++i == 5) {
+                               printk("\n" KERN_DEBUG);
+                               i=0;
+@@ -231,11 +277,8 @@
+               printk("\n");
+       });
+-      if (ic->nodes == (void *)ic) {
+-              D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
++      if (ic->nodes == (void *)ic)
+               jffs2_del_ino_cache(c, ic);
+-              jffs2_free_inode_cache(ic);
+-      }
+ }
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+@@ -256,118 +299,148 @@
+       jeb->last_node = NULL;
+ }
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+-{
+-      OFNI_BS_2SFFJ(c)->s_dirt = 1;
+-}
+-
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+-      static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
+-      struct jffs2_eraseblock *jeb;
+-      struct jffs2_raw_node_ref *marker_ref;
++      struct jffs2_raw_node_ref *marker_ref = NULL;
+       unsigned char *ebuf;
+-      ssize_t retlen;
++      size_t retlen;
+       int ret;
++      uint32_t bad_offset;
+-      marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+-
+-      spin_lock_bh(&c->erase_completion_lock);
+-      while (!list_empty(&c->erase_complete_list)) {
+-              jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+-              list_del(&jeb->list);
+-              spin_unlock_bh(&c->erase_completion_lock);
+-
++      if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) {
+               marker_ref = jffs2_alloc_raw_node_ref();
+               if (!marker_ref) {
+                       printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
+-                      /* Come back later */
++                      /* Stick it back on the list from whence it came and come back later */
+                       jffs2_erase_pending_trigger(c);
++                      spin_lock(&c->erase_completion_lock);
++                      list_add(&jeb->list, &c->erase_complete_list);
++                      spin_unlock(&c->erase_completion_lock);
+                       return;
+               }
+-
++      }
+               ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+               if (!ebuf) {
+                       printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+               } else {
+-                      __u32 ofs = jeb->offset;
++              uint32_t ofs = jeb->offset;
+                       D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+                       while(ofs < jeb->offset + c->sector_size) {
+-                              __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
++                      uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+                               int i;
+-                              ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
+-                              if (ret < 0) {
++                      bad_offset = ofs;
++
++                      ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
++                      if (ret) {
+                                       printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+                                       goto bad;
+                               }
+                               if (retlen != readlen) {
+-                                      printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
++                              printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+                                       goto bad;
+                               }
+                               for (i=0; i<readlen; i += sizeof(unsigned long)) {
+                                       /* It's OK. We know it's properly aligned */
+                                       unsigned long datum = *(unsigned long *)(&ebuf[i]);
+                                       if (datum + 1) {
+-                                              printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
++                                      bad_offset += i;
++                                      printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
+                                       bad: 
++                                      if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
+                                               jffs2_free_raw_node_ref(marker_ref);
+                                               kfree(ebuf);
+                                       bad2:
+-                                              spin_lock_bh(&c->erase_completion_lock);
+-                                              c->erasing_size -= c->sector_size;
+-                                              c->bad_size += c->sector_size;
+-
+-                                              list_add_tail(&jeb->list, &c->bad_list);
+-                                              c->nr_erasing_blocks--;
+-                                              spin_unlock_bh(&c->erase_completion_lock);
+-                                              wake_up(&c->erase_wait);
++                                      spin_lock(&c->erase_completion_lock);
++                                      /* Stick it on a list (any list) so
++                                         erase_failed can take it right off
++                                         again.  Silly, but shouldn't happen
++                                         often. */
++                                      list_add(&jeb->list, &c->erasing_list);
++                                      spin_unlock(&c->erase_completion_lock);
++                                      jffs2_erase_failed(c, jeb, bad_offset);
+                                               return;
+                                       }
+                               }
+                               ofs += readlen;
++                      cond_resched();
+                       }
+                       kfree(ebuf);
+               }
+                                       
++      bad_offset = jeb->offset;
++
+               /* Write the erase complete marker */   
+               D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+-              ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
++      if (jffs2_cleanmarker_oob(c)) {
++
++              if (jffs2_write_nand_cleanmarker(c, jeb))
++                      goto bad2;
++                      
++              jeb->first_node = jeb->last_node = NULL;
++
++              jeb->free_size = c->sector_size;
++              jeb->used_size = 0;
++              jeb->dirty_size = 0;
++              jeb->wasted_size = 0;
++      } else if (c->cleanmarker_size == 0) {
++              jeb->first_node = jeb->last_node = NULL;
++
++              jeb->free_size = c->sector_size;
++              jeb->used_size = 0;
++              jeb->dirty_size = 0;
++              jeb->wasted_size = 0;
++      } else {
++              struct kvec vecs[1];
++              struct jffs2_unknown_node marker = {
++                      .magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
++                      .nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
++                      .totlen =       cpu_to_je32(c->cleanmarker_size)
++              };
++
++              marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++
++              vecs[0].iov_base = (unsigned char *) &marker;
++              vecs[0].iov_len = sizeof(marker);
++              ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
++              
+               if (ret) {
+                       printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+                              jeb->offset, ret);
+                       goto bad2;
+               }
+               if (retlen != sizeof(marker)) {
+-                      printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
++                      printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+                              jeb->offset, sizeof(marker), retlen);
+                       goto bad2;
+               }
+               marker_ref->next_in_ino = NULL;
+               marker_ref->next_phys = NULL;
+-              marker_ref->flash_offset = jeb->offset;
+-              marker_ref->totlen = PAD(sizeof(marker));
++              marker_ref->flash_offset = jeb->offset | REF_NORMAL;
++              marker_ref->__totlen = c->cleanmarker_size;
+               jeb->first_node = jeb->last_node = marker_ref;
+-              jeb->free_size = c->sector_size - marker_ref->totlen;
+-              jeb->used_size = marker_ref->totlen;
++              jeb->free_size = c->sector_size - c->cleanmarker_size;
++              jeb->used_size = c->cleanmarker_size;
+               jeb->dirty_size = 0;
++              jeb->wasted_size = 0;
++      }
+-              spin_lock_bh(&c->erase_completion_lock);
++      spin_lock(&c->erase_completion_lock);
+               c->erasing_size -= c->sector_size;
+               c->free_size += jeb->free_size;
+               c->used_size += jeb->used_size;
+               ACCT_SANITY_CHECK(c,jeb);
+-              ACCT_PARANOIA_CHECK(jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
+               list_add_tail(&jeb->list, &c->free_list);
+               c->nr_erasing_blocks--;
+               c->nr_free_blocks++;
++      spin_unlock(&c->erase_completion_lock);
+               wake_up(&c->erase_wait);
+-      }
+-      spin_unlock_bh(&c->erase_completion_lock);
+ }
++
+--- linux-2.4.21/fs/jffs2/file.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/file.c
+@@ -1,319 +1,106 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
++#include <linux/version.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/time.h>
+ #include <linux/pagemap.h>
++#include <linux/highmem.h>
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include "nodelist.h"
+-#include "crc32.h"
+ extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
+ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
+-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
+ {
+-      /* Move along. Nothing to see here */
++      struct inode *inode = dentry->d_inode;
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++
++      /* Trigger GC to flush any pending writes for this inode */
++      jffs2_flush_wbuf_gc(c, inode->i_ino);
++                      
+       return 0;
+ }
+ struct file_operations jffs2_file_operations =
+ {
+-      llseek:         generic_file_llseek,
+-      open:           generic_file_open,
+-      read:           generic_file_read,
+-      write:          generic_file_write,
+-      ioctl:          jffs2_ioctl,
+-      mmap:           generic_file_mmap,
+-      fsync:          jffs2_null_fsync
++      .llseek =       generic_file_llseek,
++      .open =         generic_file_open,
++      .read =         generic_file_read,
++      .write =        generic_file_write,
++      .ioctl =        jffs2_ioctl,
++      .mmap =         generic_file_readonly_mmap,
++      .fsync =        jffs2_fsync,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
++      .sendfile =     generic_file_sendfile
++#endif
+ };
+ /* jffs2_file_inode_operations */
+ struct inode_operations jffs2_file_inode_operations =
+ {
+-      setattr:        jffs2_setattr
++      .setattr =      jffs2_setattr
+ };
+ struct address_space_operations jffs2_file_address_operations =
+ {
+-      readpage:       jffs2_readpage,
+-      prepare_write:  jffs2_prepare_write,
+-      commit_write:   jffs2_commit_write
++      .readpage =     jffs2_readpage,
++      .prepare_write =jffs2_prepare_write,
++      .commit_write = jffs2_commit_write
+ };
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+-{
+-      struct jffs2_full_dnode *old_metadata, *new_metadata;
+-      struct inode *inode = dentry->d_inode;
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+-      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      struct jffs2_raw_inode *ri;
+-      unsigned short dev;
+-      unsigned char *mdata = NULL;
+-      int mdatalen = 0;
+-      unsigned int ivalid;
+-      __u32 phys_ofs, alloclen;
+-      int ret;
+-      D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+-      ret = inode_change_ok(inode, iattr);
+-      if (ret) 
+-              return ret;
+-
+-      /* Special cases - we don't want more than one data node
+-         for these types on the medium at any time. So setattr
+-         must read the original data associated with the node
+-         (i.e. the device numbers or the target name) and write
+-         it out again with the appropriate data attached */
+-      if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+-              /* For these, we don't actually need to read the old node */
+-              dev =  (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) | 
+-                      MINOR(to_kdev_t(dentry->d_inode->i_rdev));
+-              mdata = (char *)&dev;
+-              mdatalen = sizeof(dev);
+-              D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+-      } else if (S_ISLNK(inode->i_mode)) {
+-              mdatalen = f->metadata->size;
+-              mdata = kmalloc(f->metadata->size, GFP_USER);
+-              if (!mdata)
+-                      return -ENOMEM;
+-              ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
+-              if (ret) {
+-                      kfree(mdata);
+-                      return ret;
+-              }
+-              D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+-      }
+-
+-      ri = jffs2_alloc_raw_inode();
+-      if (!ri) {
+-              if (S_ISLNK(inode->i_mode))
+-                      kfree(mdata);
+-              return -ENOMEM;
+-      }
+-              
+-      ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+-      if (ret) {
+-              jffs2_free_raw_inode(ri);
+-              if (S_ISLNK(inode->i_mode))
+-                       kfree(mdata);
+-              return ret;
+-      }
+-      down(&f->sem);
+-        ivalid = iattr->ia_valid;
+-      
+-      ri->magic = JFFS2_MAGIC_BITMASK;
+-      ri->nodetype = JFFS2_NODETYPE_INODE;
+-      ri->totlen = sizeof(*ri) + mdatalen;
+-      ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+-      ri->ino = inode->i_ino;
+-      ri->version = ++f->highest_version;
+-
+-      ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode;
+-      ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid;
+-      ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid;
+-
+-      if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+-          !in_group_p(ri->gid) && !capable(CAP_FSETID))
+-              ri->mode &= ~S_ISGID;
+-
+-      ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size;
+-      ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime;
+-      ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime;
+-      ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime;
+-
+-      ri->offset = 0;
+-      ri->csize = ri->dsize = mdatalen;
+-      ri->compr = JFFS2_COMPR_NONE;
+-      if (inode->i_size < ri->isize) {
+-              /* It's an extension. Make it a hole node */
+-              ri->compr = JFFS2_COMPR_ZERO;
+-              ri->dsize = ri->isize - inode->i_size;
+-              ri->offset = inode->i_size;
+-      }
+-      ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+-      if (mdatalen)
+-              ri->data_crc = crc32(0, mdata, mdatalen);
+-      else
+-              ri->data_crc = 0;
+-
+-      new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+-      if (S_ISLNK(inode->i_mode))
+-              kfree(mdata);
+-
+-      jffs2_complete_reservation(c);
+-      
+-      if (IS_ERR(new_metadata)) {
+-              jffs2_free_raw_inode(ri);
+-              up(&f->sem);
+-              return PTR_ERR(new_metadata);
+-      }
+-      /* It worked. Update the inode */
+-      inode->i_atime = ri->atime;
+-      inode->i_ctime = ri->ctime;
+-      inode->i_mtime = ri->mtime;
+-      inode->i_mode = ri->mode;
+-      inode->i_uid = ri->uid;
+-      inode->i_gid = ri->gid;
+-
+-
+-      old_metadata = f->metadata;
+-
+-      if (inode->i_size > ri->isize) {
+-              vmtruncate(inode, ri->isize);
+-              jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
+-      }
+-
+-      if (inode->i_size < ri->isize) {
+-              jffs2_add_full_dnode_to_inode(c, f, new_metadata);
+-              inode->i_size = ri->isize;
+-              f->metadata = NULL;
+-      } else {
+-              f->metadata = new_metadata;
+-      }
+-      if (old_metadata) {
+-              jffs2_mark_node_obsolete(c, old_metadata->raw);
+-              jffs2_free_full_dnode(old_metadata);
+-      }
+-      jffs2_free_raw_inode(ri);
+-      up(&f->sem);
+-      return 0;
+-}
+-
+ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
+ {
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      struct jffs2_node_frag *frag = f->fraglist;
+-      __u32 offset = pg->index << PAGE_CACHE_SHIFT;
+-      __u32 end = offset + PAGE_CACHE_SIZE;
+       unsigned char *pg_buf;
+       int ret;
+-      D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
++      D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
+       if (!PageLocked(pg))
+                 PAGE_BUG(pg);
+-      while(frag && frag->ofs + frag->size  <= offset) {
+-              //              D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
+-              frag = frag->next;
+-      }
+-
+       pg_buf = kmap(pg);
++      /* FIXME: Can kmap fail? */
+-      /* XXX FIXME: Where a single physical node actually shows up in two
+-         frags, we read it twice. Don't do that. */
+-      /* Now we're pointing at the first frag which overlaps our page */
+-      while(offset < end) {
+-              D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end));
+-              if (!frag || frag->ofs > offset) {
+-                      __u32 holesize = end - offset;
+-                      if (frag) {
+-                              D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
+-                              holesize = min(holesize, frag->ofs - offset);
+-                              D1(jffs2_print_frag_list(f));
+-                      }
+-                      D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+-                      memset(pg_buf, 0, holesize);
+-                      pg_buf += holesize;
+-                      offset += holesize;
+-                      continue;
+-              } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
+-                      D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+-                                inode->i_ino, frag->ofs, offset));
+-                      D1(jffs2_print_frag_list(f));
+-                      memset(pg_buf, 0, end - offset);
+-                      ClearPageUptodate(pg);
+-                      SetPageError(pg);
+-                      kunmap(pg);
+-                      return -EIO;
+-              } else if (!frag->node) {
+-                      __u32 holeend = min(end, frag->ofs + frag->size);
+-                      D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+-                      memset(pg_buf, 0, holeend - offset);
+-                      pg_buf += holeend - offset;
+-                      offset = holeend;
+-                      frag = frag->next;
+-                      continue;
+-              } else {
+-                      __u32 readlen;
+-                      __u32 fragofs; /* offset within the frag to start reading */
++      ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+-                      fragofs = offset - frag->ofs;
+-                      readlen = min(frag->size - fragofs, end - offset);
+-                      D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs, 
+-                                fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
+-                      ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+-                      D2(printk(KERN_DEBUG "node read done\n"));
+                       if (ret) {
+-                              D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
+-                              memset(pg_buf, 0, readlen);
+                               ClearPageUptodate(pg);
+                               SetPageError(pg);
+-                              kunmap(pg);
+-                              return ret;
+-                      }
+-              
+-                      pg_buf += readlen;
+-                      offset += readlen;
+-                      frag = frag->next;
+-                      D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+-              }
+-      }
+-      D2(printk(KERN_DEBUG "readpage finishing\n"));
++      } else {
+       SetPageUptodate(pg);
+       ClearPageError(pg);
++      }
+       flush_dcache_page(pg);
+-
+       kunmap(pg);
+-      D1(printk(KERN_DEBUG "readpage finished\n"));
++
++      D2(printk(KERN_DEBUG "readpage finished\n"));
+       return 0;
+ }
+ int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
+ {
+       int ret = jffs2_do_readpage_nolock(inode, pg);
+-      UnlockPage(pg);
++      unlock_page(pg);
+       return ret;
+ }
+@@ -333,17 +120,17 @@
+ {
+       struct inode *inode = pg->mapping->host;
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+-      __u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
++      uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
+       int ret = 0;
+-      D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
++      D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
+       if (pageofs > inode->i_size) {
+               /* Make new hole frag from old EOF to new page */
+               struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+               struct jffs2_raw_inode ri;
+               struct jffs2_full_dnode *fn;
+-              __u32 phys_ofs, alloc_len;
++              uint32_t phys_ofs, alloc_len;
+               
+               D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+                         (unsigned int)inode->i_size, pageofs));
+@@ -355,29 +142,30 @@
+               down(&f->sem);
+               memset(&ri, 0, sizeof(ri));
+-              ri.magic = JFFS2_MAGIC_BITMASK;
+-              ri.nodetype = JFFS2_NODETYPE_INODE;
+-              ri.totlen = sizeof(ri);
+-              ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
++              ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++              ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++              ri.totlen = cpu_to_je32(sizeof(ri));
++              ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+-              ri.ino = f->inocache->ino;
+-              ri.version = ++f->highest_version;
+-              ri.mode = inode->i_mode;
+-              ri.uid = inode->i_uid;
+-              ri.gid = inode->i_gid;
+-              ri.isize = max((__u32)inode->i_size, pageofs);
+-              ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
+-              ri.offset = inode->i_size;
+-              ri.dsize = pageofs - inode->i_size;
+-              ri.csize = 0;
++              ri.ino = cpu_to_je32(f->inocache->ino);
++              ri.version = cpu_to_je32(++f->highest_version);
++              ri.mode = cpu_to_jemode(inode->i_mode);
++              ri.uid = cpu_to_je16(inode->i_uid);
++              ri.gid = cpu_to_je16(inode->i_gid);
++              ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
++              ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
++              ri.offset = cpu_to_je32(inode->i_size);
++              ri.dsize = cpu_to_je32(pageofs - inode->i_size);
++              ri.csize = cpu_to_je32(0);
+               ri.compr = JFFS2_COMPR_ZERO;
+-              ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+-              ri.data_crc = 0;
++              ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++              ri.data_crc = cpu_to_je32(0);
++              
++              fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+               
+-              fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+-              jffs2_complete_reservation(c);
+               if (IS_ERR(fn)) {
+                       ret = PTR_ERR(fn);
++                      jffs2_complete_reservation(c);
+                       up(&f->sem);
+                       return ret;
+               }
+@@ -391,16 +179,17 @@
+                       D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+                       jffs2_mark_node_obsolete(c, fn->raw);
+                       jffs2_free_full_dnode(fn);
++                      jffs2_complete_reservation(c);
+                       up(&f->sem);
+                       return ret;
+               }
++              jffs2_complete_reservation(c);
+               inode->i_size = pageofs;
+               up(&f->sem);
+       }
+       
+-
+       /* Read in the page if it wasn't already present, unless it's a whole page */
+-      if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
++      if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+               down(&f->sem);
+               ret = jffs2_do_readpage_nolock(inode, pg);
+               up(&f->sem);
+@@ -417,14 +206,13 @@
+       struct inode *inode = pg->mapping->host;
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
+-      __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
+-      __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
+       struct jffs2_raw_inode *ri;
++      unsigned aligned_start = start & ~3;
+       int ret = 0;
+-      ssize_t writtenlen = 0;
++      uint32_t writtenlen = 0;
+-      D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
++      D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
++                inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+       if (!start && end == PAGE_CACHE_SIZE) {
+               /* We need to avoid deadlock with page_cache_read() in
+@@ -435,109 +223,53 @@
+       }
+       ri = jffs2_alloc_raw_inode();
+-      if (!ri)
+-              return -ENOMEM;
+-
+-      while(writelen) {
+-              struct jffs2_full_dnode *fn;
+-              unsigned char *comprbuf = NULL;
+-              unsigned char comprtype = JFFS2_COMPR_NONE;
+-              __u32 phys_ofs, alloclen;
+-              __u32 datalen, cdatalen;
+-              D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+-
+-              ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+-              if (ret) {
+-                      SetPageError(pg);
+-                      D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+-                      break;
+-              }
+-              down(&f->sem);
+-              datalen = writelen;
+-              cdatalen = min(alloclen - sizeof(*ri), writelen);
+-
+-              comprbuf = kmalloc(cdatalen, GFP_KERNEL);
+-              if (comprbuf) {
+-                      comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
+-              }
+-              if (comprtype == JFFS2_COMPR_NONE) {
+-                      /* Either compression failed, or the allocation of comprbuf failed */
+-                      if (comprbuf)
+-                              kfree(comprbuf);
+-                      comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
+-                      datalen = cdatalen;
++      if (!ri) {
++              D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
++              return -ENOMEM;
+               }
+-              /* Now comprbuf points to the data to be written, be it compressed or not.
+-                 comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
+-                 that the comprbuf doesn't need to be kfree()d. 
+-              */
+-              ri->magic = JFFS2_MAGIC_BITMASK;
+-              ri->nodetype = JFFS2_NODETYPE_INODE;
+-              ri->totlen = sizeof(*ri) + cdatalen;
+-              ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+-              ri->ino = inode->i_ino;
+-              ri->version = ++f->highest_version;
+-              ri->mode = inode->i_mode;
+-              ri->uid = inode->i_uid;
+-              ri->gid = inode->i_gid;
+-              ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
+-              ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
+-              ri->offset = file_ofs;
+-              ri->csize = cdatalen;
+-              ri->dsize = datalen;
+-              ri->compr = comprtype;
+-              ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+-              ri->data_crc = crc32(0, comprbuf, cdatalen);
++      /* Set the fields that the generic jffs2_write_inode_range() code can't find */
++      ri->ino = cpu_to_je32(inode->i_ino);
++      ri->mode = cpu_to_jemode(inode->i_mode);
++      ri->uid = cpu_to_je16(inode->i_uid);
++      ri->gid = cpu_to_je16(inode->i_gid);
++      ri->isize = cpu_to_je32((uint32_t)inode->i_size);
++      ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
+-              fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
++      /* In 2.4, it was already kmapped by generic_file_write(). Doesn't
++         hurt to do it again. The alternative is ifdefs, which are ugly. */
++      kmap(pg);
+-              jffs2_complete_reservation(c);
++      ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
++                                    (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
++                                    end - aligned_start, &writtenlen);
+-              if (comprtype != JFFS2_COMPR_NONE)
+-                      kfree(comprbuf);
++      kunmap(pg);
+-              if (IS_ERR(fn)) {
+-                      ret = PTR_ERR(fn);
+-                      up(&f->sem);
+-                      SetPageError(pg);
+-                      break;
+-              }
+-              ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+-              if (f->metadata) {
+-                      jffs2_mark_node_obsolete(c, f->metadata->raw);
+-                      jffs2_free_full_dnode(f->metadata);
+-                      f->metadata = NULL;
+-              }
+-              up(&f->sem);
+               if (ret) {
+-                      /* Eep */
+-                      D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+-                      jffs2_mark_node_obsolete(c, fn->raw);
+-                      jffs2_free_full_dnode(fn);
++              /* There was an error writing. */
+                       SetPageError(pg);
+-                      break;
+               }
+-              inode->i_size = ri->isize;
++      
++      /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
++      if (writtenlen < (start&3))
++              writtenlen = 0;
++      else
++              writtenlen -= (start&3);
++
++      if (writtenlen) {
++              if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
++                      inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
+               inode->i_blocks = (inode->i_size + 511) >> 9;
+-              inode->i_ctime = inode->i_mtime = ri->ctime;
+-              if (!datalen) {
+-                      printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
+-                      ret = -EIO;
+-                      SetPageError(pg);
+-                      break;
++                      
++                      inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
+               }
+-              D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+-              writtenlen += datalen;
+-              file_ofs += datalen;
+-              writelen -= datalen;
+       }
+       jffs2_free_raw_inode(ri);
+-      if (writtenlen < end) {
++      if (start+writtenlen < end) {
+               /* generic_file_write has written more to the page cache than we've
+                  actually written to the medium. Mark the page !Uptodate so that 
+                  it gets reread */
+@@ -545,13 +277,7 @@
+               SetPageError(pg);
+               ClearPageUptodate(pg);
+       }
+-      if (writtenlen <= start) {
+-              /* We didn't even get to the start of the affected part */
+-              ret = ret?ret:-ENOSPC;
+-              D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
+-      }
+-      writtenlen = min(end-start, writtenlen-start);
+-      D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
++      D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
+       return writtenlen?writtenlen:ret;
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/fs.c
+@@ -0,0 +1,693 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/mtd/mtd.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/vfs.h>
++#include <linux/crc32.h>
++#include "nodelist.h"
++
++
++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
++{
++      struct jffs2_full_dnode *old_metadata, *new_metadata;
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++      struct jffs2_raw_inode *ri;
++      unsigned short dev;
++      unsigned char *mdata = NULL;
++      int mdatalen = 0;
++      unsigned int ivalid;
++      uint32_t phys_ofs, alloclen;
++      int ret;
++      D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
++      ret = inode_change_ok(inode, iattr);
++      if (ret) 
++              return ret;
++
++      /* Special cases - we don't want more than one data node
++         for these types on the medium at any time. So setattr
++         must read the original data associated with the node
++         (i.e. the device numbers or the target name) and write
++         it out again with the appropriate data attached */
++      if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++              /* For these, we don't actually need to read the old node */
++              dev = old_encode_dev(inode->i_rdev);
++              mdata = (char *)&dev;
++              mdatalen = sizeof(dev);
++              D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
++      } else if (S_ISLNK(inode->i_mode)) {
++              mdatalen = f->metadata->size;
++              mdata = kmalloc(f->metadata->size, GFP_USER);
++              if (!mdata)
++                      return -ENOMEM;
++              ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
++              if (ret) {
++                      kfree(mdata);
++                      return ret;
++              }
++              D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
++      }
++
++      ri = jffs2_alloc_raw_inode();
++      if (!ri) {
++              if (S_ISLNK(inode->i_mode))
++                      kfree(mdata);
++              return -ENOMEM;
++      }
++              
++      ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++      if (ret) {
++              jffs2_free_raw_inode(ri);
++              if (S_ISLNK(inode->i_mode & S_IFMT))
++                       kfree(mdata);
++              return ret;
++      }
++      down(&f->sem);
++      ivalid = iattr->ia_valid;
++      
++      ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++      ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
++      ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++      ri->ino = cpu_to_je32(inode->i_ino);
++      ri->version = cpu_to_je32(++f->highest_version);
++
++      ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
++      ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
++
++      if (ivalid & ATTR_MODE)
++              if (iattr->ia_mode & S_ISGID &&
++                  !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
++                      ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
++              else 
++                      ri->mode = cpu_to_jemode(iattr->ia_mode);
++      else
++              ri->mode = cpu_to_jemode(inode->i_mode);
++
++
++      ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
++      ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
++      ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
++      ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
++
++      ri->offset = cpu_to_je32(0);
++      ri->csize = ri->dsize = cpu_to_je32(mdatalen);
++      ri->compr = JFFS2_COMPR_NONE;
++      if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++              /* It's an extension. Make it a hole node */
++              ri->compr = JFFS2_COMPR_ZERO;
++              ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
++              ri->offset = cpu_to_je32(inode->i_size);
++      }
++      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++      if (mdatalen)
++              ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
++      else
++              ri->data_crc = cpu_to_je32(0);
++
++      new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
++      if (S_ISLNK(inode->i_mode))
++              kfree(mdata);
++      
++      if (IS_ERR(new_metadata)) {
++              jffs2_complete_reservation(c);
++              jffs2_free_raw_inode(ri);
++              up(&f->sem);
++              return PTR_ERR(new_metadata);
++      }
++      /* It worked. Update the inode */
++      inode->i_atime = ITIME(je32_to_cpu(ri->atime));
++      inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
++      inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
++      inode->i_mode = jemode_to_cpu(ri->mode);
++      inode->i_uid = je16_to_cpu(ri->uid);
++      inode->i_gid = je16_to_cpu(ri->gid);
++
++
++      old_metadata = f->metadata;
++
++      if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++              jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
++
++      if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++              jffs2_add_full_dnode_to_inode(c, f, new_metadata);
++              inode->i_size = iattr->ia_size;
++              f->metadata = NULL;
++      } else {
++              f->metadata = new_metadata;
++      }
++      if (old_metadata) {
++              jffs2_mark_node_obsolete(c, old_metadata->raw);
++              jffs2_free_full_dnode(old_metadata);
++      }
++      jffs2_free_raw_inode(ri);
++
++      up(&f->sem);
++      jffs2_complete_reservation(c);
++
++      /* We have to do the vmtruncate() without f->sem held, since
++         some pages may be locked and waiting for it in readpage(). 
++         We are protected from a simultaneous write() extending i_size
++         back past iattr->ia_size, because do_truncate() holds the
++         generic inode semaphore. */
++      if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++              vmtruncate(inode, iattr->ia_size);
++
++      return 0;
++}
++
++int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
++{
++      return jffs2_do_setattr(dentry->d_inode, iattr);
++}
++
++int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
++{
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++      unsigned long avail;
++
++      buf->f_type = JFFS2_SUPER_MAGIC;
++      buf->f_bsize = 1 << PAGE_SHIFT;
++      buf->f_blocks = c->flash_size >> PAGE_SHIFT;
++      buf->f_files = 0;
++      buf->f_ffree = 0;
++      buf->f_namelen = JFFS2_MAX_NAME_LEN;
++
++      spin_lock(&c->erase_completion_lock);
++
++      avail = c->dirty_size + c->free_size;
++      if (avail > c->sector_size * c->resv_blocks_write)
++              avail -= c->sector_size * c->resv_blocks_write;
++      else
++              avail = 0;
++
++      buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++
++      D2(jffs2_dump_block_lists(c));
++
++      spin_unlock(&c->erase_completion_lock);
++
++      return 0;
++}
++
++
++void jffs2_clear_inode (struct inode *inode)
++{
++      /* We can forget about this inode for now - drop all 
++       *  the nodelists associated with it, etc.
++       */
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++      
++      D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
++
++      jffs2_do_clear_inode(c, f);
++}
++
++void jffs2_read_inode (struct inode *inode)
++{
++      struct jffs2_inode_info *f;
++      struct jffs2_sb_info *c;
++      struct jffs2_raw_inode latest_node;
++      int ret;
++
++      D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++
++      f = JFFS2_INODE_INFO(inode);
++      c = JFFS2_SB_INFO(inode->i_sb);
++
++      jffs2_init_inode_info(f);
++      
++      ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
++
++      if (ret) {
++              make_bad_inode(inode);
++              up(&f->sem);
++              return;
++      }
++      inode->i_mode = jemode_to_cpu(latest_node.mode);
++      inode->i_uid = je16_to_cpu(latest_node.uid);
++      inode->i_gid = je16_to_cpu(latest_node.gid);
++      inode->i_size = je32_to_cpu(latest_node.isize);
++      inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
++      inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
++      inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
++
++      inode->i_nlink = f->inocache->nlink;
++
++      inode->i_blksize = PAGE_SIZE;
++      inode->i_blocks = (inode->i_size + 511) >> 9;
++      
++      switch (inode->i_mode & S_IFMT) {
++              jint16_t rdev;
++
++      case S_IFLNK:
++              inode->i_op = &jffs2_symlink_inode_operations;
++              break;
++              
++      case S_IFDIR:
++      {
++              struct jffs2_full_dirent *fd;
++
++              for (fd=f->dents; fd; fd = fd->next) {
++                      if (fd->type == DT_DIR && fd->ino)
++                              inode->i_nlink++;
++              }
++              /* and '..' */
++              inode->i_nlink++;
++              /* Root dir gets i_nlink 3 for some reason */
++              if (inode->i_ino == 1)
++                      inode->i_nlink++;
++
++              inode->i_op = &jffs2_dir_inode_operations;
++              inode->i_fop = &jffs2_dir_operations;
++              break;
++      }
++      case S_IFREG:
++              inode->i_op = &jffs2_file_inode_operations;
++              inode->i_fop = &jffs2_file_operations;
++              inode->i_mapping->a_ops = &jffs2_file_address_operations;
++              inode->i_mapping->nrpages = 0;
++              break;
++
++      case S_IFBLK:
++      case S_IFCHR:
++              /* Read the device numbers from the media */
++              D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
++              if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
++                      /* Eep */
++                      printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
++                      up(&f->sem);
++                      jffs2_do_clear_inode(c, f);
++                      make_bad_inode(inode);
++                      return;
++              }                       
++
++      case S_IFSOCK:
++      case S_IFIFO:
++              inode->i_op = &jffs2_file_inode_operations;
++              init_special_inode(inode, inode->i_mode,
++                                 old_decode_dev((je16_to_cpu(rdev))));
++              break;
++
++      default:
++              printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
++      }
++
++      up(&f->sem);
++
++      D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++}
++
++void jffs2_dirty_inode(struct inode *inode)
++{
++      struct iattr iattr;
++
++      if (!(inode->i_state & I_DIRTY_DATASYNC)) {
++              D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
++              return;
++      }
++
++      D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
++
++      iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
++      iattr.ia_mode = inode->i_mode;
++      iattr.ia_uid = inode->i_uid;
++      iattr.ia_gid = inode->i_gid;
++      iattr.ia_atime = inode->i_atime;
++      iattr.ia_mtime = inode->i_mtime;
++      iattr.ia_ctime = inode->i_ctime;
++
++      jffs2_do_setattr(inode, &iattr);
++}
++
++int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
++{
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++      if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
++              return -EROFS;
++
++      /* We stop if it was running, then restart if it needs to.
++         This also catches the case where it was stopped and this
++         is just a remount to restart it.
++         Flush the writebuffer, if neccecary, else we loose it */
++      if (!(sb->s_flags & MS_RDONLY)) {
++              jffs2_stop_garbage_collect_thread(c);
++              down(&c->alloc_sem);
++              jffs2_flush_wbuf_pad(c);
++              up(&c->alloc_sem);
++      }       
++
++      if (!(*flags & MS_RDONLY))
++              jffs2_start_garbage_collect_thread(c);
++      
++      *flags |= MS_NOATIME;
++
++      return 0;
++}
++
++void jffs2_write_super (struct super_block *sb)
++{
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++      sb->s_dirt = 0;
++
++      if (sb->s_flags & MS_RDONLY)
++              return;
++
++      D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
++      jffs2_garbage_collect_trigger(c);
++      jffs2_erase_pending_blocks(c, 0);
++      jffs2_flush_wbuf_gc(c, 0);
++}
++
++
++/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
++   fill in the raw_inode while you're at it. */
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++{
++      struct inode *inode;
++      struct super_block *sb = dir_i->i_sb;
++      struct jffs2_sb_info *c;
++      struct jffs2_inode_info *f;
++      int ret;
++
++      D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
++
++      c = JFFS2_SB_INFO(sb);
++      
++      inode = new_inode(sb);
++      
++      if (!inode)
++              return ERR_PTR(-ENOMEM);
++
++      f = JFFS2_INODE_INFO(inode);
++      jffs2_init_inode_info(f);
++
++      memset(ri, 0, sizeof(*ri));
++      /* Set OS-specific defaults for new inodes */
++      ri->uid = cpu_to_je16(current->fsuid);
++
++      if (dir_i->i_mode & S_ISGID) {
++              ri->gid = cpu_to_je16(dir_i->i_gid);
++              if (S_ISDIR(mode))
++                      mode |= S_ISGID;
++      } else {
++              ri->gid = cpu_to_je16(current->fsgid);
++      }
++      ri->mode =  cpu_to_jemode(mode);
++      ret = jffs2_do_new_inode (c, f, mode, ri);
++      if (ret) {
++              make_bad_inode(inode);
++              iput(inode);
++              return ERR_PTR(ret);
++      }
++      inode->i_nlink = 1;
++      inode->i_ino = je32_to_cpu(ri->ino);
++      inode->i_mode = jemode_to_cpu(ri->mode);
++      inode->i_gid = je16_to_cpu(ri->gid);
++      inode->i_uid = je16_to_cpu(ri->uid);
++      inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
++      ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
++
++      inode->i_blksize = PAGE_SIZE;
++      inode->i_blocks = 0;
++      inode->i_size = 0;
++
++      insert_inode_hash(inode);
++
++      return inode;
++}
++
++
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
++{
++      struct jffs2_sb_info *c;
++      struct inode *root_i;
++      int ret;
++      size_t blocks;
++
++      c = JFFS2_SB_INFO(sb);
++
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++      if (c->mtd->type == MTD_NANDFLASH) {
++              printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
++              return -EINVAL;
++      }
++      if (c->mtd->type == MTD_DATAFLASH) {
++              printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
++              return -EINVAL;
++      }
++#endif
++
++      c->flash_size = c->mtd->size;
++
++      /* 
++       * Check, if we have to concatenate physical blocks to larger virtual blocks
++       * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
++       */
++      c->sector_size = c->mtd->erasesize; 
++      blocks = c->flash_size / c->sector_size;
++      if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
++              while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
++                      blocks >>= 1;
++                      c->sector_size <<= 1;
++              }       
++      }
++
++      /*
++       * Size alignment check
++       */
++      if ((c->sector_size * blocks) != c->flash_size) {
++              c->flash_size = c->sector_size * blocks;                
++              printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
++                      c->flash_size / 1024);
++      }
++
++      if (c->sector_size != c->mtd->erasesize)
++              printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", 
++                      c->mtd->erasesize / 1024, c->sector_size / 1024);
++
++      if (c->flash_size < 5*c->sector_size) {
++              printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
++              return -EINVAL;
++      }
++
++      c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
++      /* Joern -- stick alignment for weird 8-byte-page flash here */
++
++      /* NAND (or other bizarre) flash... do setup accordingly */
++      ret = jffs2_flash_setup(c);
++      if (ret)
++              return ret;
++
++      c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
++      if (!c->inocache_list) {
++              ret = -ENOMEM;
++              goto out_wbuf;
++      }
++      memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
++
++      if ((ret = jffs2_do_mount_fs(c)))
++              goto out_inohash;
++
++      ret = -EINVAL;
++
++      D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
++      root_i = iget(sb, 1);
++      if (is_bad_inode(root_i)) {
++              D1(printk(KERN_WARNING "get root inode failed\n"));
++              goto out_nodes;
++      }
++
++      D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
++      sb->s_root = d_alloc_root(root_i);
++      if (!sb->s_root)
++              goto out_root_i;
++
++#if LINUX_VERSION_CODE >= 0x20403
++      sb->s_maxbytes = 0xFFFFFFFF;
++#endif
++      sb->s_blocksize = PAGE_CACHE_SIZE;
++      sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++      sb->s_magic = JFFS2_SUPER_MAGIC;
++      if (!(sb->s_flags & MS_RDONLY))
++              jffs2_start_garbage_collect_thread(c);
++      return 0;
++
++ out_root_i:
++      iput(root_i);
++ out_nodes:
++      jffs2_free_ino_caches(c);
++      jffs2_free_raw_node_refs(c);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
++              kfree(c->blocks);
++ out_inohash:
++      kfree(c->inocache_list);
++ out_wbuf:
++      jffs2_flash_cleanup(c);
++
++      return ret;
++}
++
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++                                 struct jffs2_inode_info *f)
++{
++      iput(OFNI_EDONI_2SFFJ(f));
++}
++
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++                                                   int inum, int nlink)
++{
++      struct inode *inode;
++      struct jffs2_inode_cache *ic;
++      if (!nlink) {
++              /* The inode has zero nlink but its nodes weren't yet marked
++                 obsolete. This has to be because we're still waiting for 
++                 the final (close() and) iput() to happen.
++
++                 There's a possibility that the final iput() could have 
++                 happened while we were contemplating. In order to ensure
++                 that we don't cause a new read_inode() (which would fail)
++                 for the inode in question, we use ilookup() in this case
++                 instead of iget().
++
++                 The nlink can't _become_ zero at this point because we're 
++                 holding the alloc_sem, and jffs2_do_unlink() would also
++                 need that while decrementing nlink on any inode.
++              */
++              inode = ilookup(OFNI_BS_2SFFJ(c), inum);
++              if (!inode) {
++                      D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
++                                inum));
++
++                      spin_lock(&c->inocache_lock);
++                      ic = jffs2_get_ino_cache(c, inum);
++                      if (!ic) {
++                              D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
++                              spin_unlock(&c->inocache_lock);
++                              return NULL;
++                      }
++                      if (ic->state != INO_STATE_CHECKEDABSENT) {
++                              /* Wait for progress. Don't just loop */
++                              D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
++                                        ic->ino, ic->state));
++                              sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++                      } else {
++                              spin_unlock(&c->inocache_lock);
++                      }
++
++                      return NULL;
++              }
++      } else {
++              /* Inode has links to it still; they're not going away because
++                 jffs2_do_unlink() would need the alloc_sem and we have it.
++                 Just iget() it, and if read_inode() is necessary that's OK.
++              */
++              inode = iget(OFNI_BS_2SFFJ(c), inum);
++              if (!inode)
++                      return ERR_PTR(-ENOMEM);
++      }
++      if (is_bad_inode(inode)) {
++              printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
++                     inum, nlink);
++              /* NB. This will happen again. We need to do something appropriate here. */
++              iput(inode);
++              return ERR_PTR(-EIO);
++      }
++
++      return JFFS2_INODE_INFO(inode);
++}
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, 
++                                 struct jffs2_inode_info *f, 
++                                 unsigned long offset,
++                                 unsigned long *priv)
++{
++      struct inode *inode = OFNI_EDONI_2SFFJ(f);
++      struct page *pg;
++
++      pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, 
++                           (void *)jffs2_do_readpage_unlock, inode);
++      if (IS_ERR(pg))
++              return (void *)pg;
++      
++      *priv = (unsigned long)pg;
++      return kmap(pg);
++}
++
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++                         unsigned char *ptr,
++                         unsigned long *priv)
++{
++      struct page *pg = (void *)*priv;
++
++      kunmap(pg);
++      page_cache_release(pg);
++}
++
++int jffs2_flash_setup(struct jffs2_sb_info *c) {
++      int ret = 0;
++      
++      if (jffs2_cleanmarker_oob(c)) {
++              /* NAND flash... do setup accordingly */
++              ret = jffs2_nand_flash_setup(c);
++              if (ret)
++                      return ret;
++      }
++
++      /* add setups for other bizarre flashes here... */
++      if (jffs2_nor_ecc(c)) {
++              ret = jffs2_nor_ecc_flash_setup(c);
++              if (ret)
++                      return ret;
++      }
++      
++      /* and Dataflash */
++      if (jffs2_dataflash(c)) {
++              ret = jffs2_dataflash_setup(c);
++              if (ret)
++                      return ret;
++      }
++      
++      return ret;
++}
++
++void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
++
++      if (jffs2_cleanmarker_oob(c)) {
++              jffs2_nand_flash_cleanup(c);
++      }
++
++      /* add cleanups for other bizarre flashes here... */
++      if (jffs2_nor_ecc(c)) {
++              jffs2_nor_ecc_flash_cleanup(c);
++      }
++      
++      /* and DataFlash */
++      if (jffs2_dataflash(c)) {
++              jffs2_dataflash_cleanup(c);
++      }
++}
+--- linux-2.4.21/fs/jffs2/gc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/gc.c
+@@ -1,76 +1,68 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
++#include <linux/crc32.h>
++#include <linux/compiler.h>
++#include <linux/stat.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
++                                        struct jffs2_inode_cache *ic,
++                                        struct jffs2_raw_node_ref *raw);
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dnode *fd);
++                                      struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dirent *fd);
++                                      struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dirent *fd);
++                                      struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+-                                    struct inode *indeo, struct jffs2_full_dnode *fn,
+-                                    __u32 start, __u32 end);
++                                    struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++                                    uint32_t start, uint32_t end);
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+-                                     struct inode *inode, struct jffs2_full_dnode *fn,
+-                                     __u32 start, __u32 end);
++                                     struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++                                     uint32_t start, uint32_t end);
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
++                             struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
+ /* Called with erase_completion_lock held */
+ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
+ {
+       struct jffs2_eraseblock *ret;
+       struct list_head *nextlist = NULL;
++      int n = jiffies % 128;
+       /* Pick an eraseblock to garbage collect next. This is where we'll
+          put the clever wear-levelling algorithms. Eventually.  */
+-      if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
++      /* We possibly want to favour the dirtier blocks more when the
++         number of free blocks is low. */
++      if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
+               D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
+               nextlist = &c->bad_used_list;
+-      } else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
+-              /* Most of the time, pick one off the dirty list */
++      } else if (n < 50 && !list_empty(&c->erasable_list)) {
++              /* Note that most of them will have gone directly to be erased. 
++                 So don't favour the erasable_list _too_ much. */
++              D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
++              nextlist = &c->erasable_list;
++      } else if (n < 110 && !list_empty(&c->very_dirty_list)) {
++              /* Most of the time, pick one off the very_dirty list */
++              D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
++              nextlist = &c->very_dirty_list;
++      } else if (n < 126 && !list_empty(&c->dirty_list)) {
+               D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
+               nextlist = &c->dirty_list;
+       } else if (!list_empty(&c->clean_list)) {
+@@ -80,9 +72,16 @@
+               D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
+               nextlist = &c->dirty_list;
++      } else if (!list_empty(&c->very_dirty_list)) {
++              D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
++              nextlist = &c->very_dirty_list;
++      } else if (!list_empty(&c->erasable_list)) {
++              D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
++
++              nextlist = &c->erasable_list;
+       } else {
+-              /* Eep. Both were empty */
+-              printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
++              /* Eep. All were empty */
++              D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
+               return NULL;
+       }
+@@ -94,6 +93,17 @@
+               printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
+               BUG();
+       }
++      
++      /* Have we accidentally picked a clean block with wasted space ? */
++      if (ret->wasted_size) {
++              D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
++              ret->dirty_size += ret->wasted_size;
++              c->wasted_size -= ret->wasted_size;
++              c->dirty_size += ret->wasted_size;
++              ret->wasted_size = 0;
++      }
++
++      D2(jffs2_dump_block_lists(c));
+       return ret;
+ }
+@@ -103,21 +113,90 @@
+  */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+ {
+-      struct jffs2_eraseblock *jeb;
+       struct jffs2_inode_info *f;
++      struct jffs2_inode_cache *ic;
++      struct jffs2_eraseblock *jeb;
+       struct jffs2_raw_node_ref *raw;
+-      struct jffs2_node_frag *frag;
+-      struct jffs2_full_dnode *fn = NULL;
+-      struct jffs2_full_dirent *fd;
+-      __u32 start = 0, end = 0, nrfrags = 0;
+-      __u32 inum;
+-      struct inode *inode;
+-      int ret = 0;
++      int ret = 0, inum, nlink;
+       if (down_interruptible(&c->alloc_sem))
+               return -EINTR;
+-      spin_lock_bh(&c->erase_completion_lock);
++      for (;;) {
++              spin_lock(&c->erase_completion_lock);
++              if (!c->unchecked_size)
++                      break;
++
++              /* We can't start doing GC yet. We haven't finished checking
++                 the node CRCs etc. Do it now. */
++              
++              /* checked_ino is protected by the alloc_sem */
++              if (c->checked_ino > c->highest_ino) {
++                      printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
++                             c->unchecked_size);
++                      D2(jffs2_dump_block_lists(c));
++                      spin_unlock(&c->erase_completion_lock);
++                      BUG();
++              }
++
++              spin_unlock(&c->erase_completion_lock);
++
++              spin_lock(&c->inocache_lock);
++
++              ic = jffs2_get_ino_cache(c, c->checked_ino++);
++
++              if (!ic) {
++                      spin_unlock(&c->inocache_lock);
++                      continue;
++              }
++
++              if (!ic->nlink) {
++                      D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
++                                ic->ino));
++                      spin_unlock(&c->inocache_lock);
++                      continue;
++              }
++              switch(ic->state) {
++              case INO_STATE_CHECKEDABSENT:
++              case INO_STATE_PRESENT:
++                      D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
++                      spin_unlock(&c->inocache_lock);
++                      continue;
++
++              case INO_STATE_GC:
++              case INO_STATE_CHECKING:
++                      printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
++                      spin_unlock(&c->inocache_lock);
++                      BUG();
++
++              case INO_STATE_READING:
++                      /* We need to wait for it to finish, lest we move on
++                         and trigger the BUG() above while we haven't yet 
++                         finished checking all its nodes */
++                      D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
++                      up(&c->alloc_sem);
++                      sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++                      return 0;
++
++              default:
++                      BUG();
++
++              case INO_STATE_UNCHECKED:
++                      ;
++              }
++              ic->state = INO_STATE_CHECKING;
++              spin_unlock(&c->inocache_lock);
++
++              D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
++
++              ret = jffs2_do_crccheck_inode(c, ic);
++              if (ret)
++                      printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
++
++              jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
++              up(&c->alloc_sem);
++              return ret;
++      }
+       /* First, work out which block we're garbage-collecting */
+       jeb = c->gcblock;
+@@ -126,13 +205,15 @@
+               jeb = jffs2_find_gc_block(c);
+       if (!jeb) {
+-              printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+-              spin_unlock_bh(&c->erase_completion_lock);
++              D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
++              spin_unlock(&c->erase_completion_lock);
+               up(&c->alloc_sem);
+               return -EIO;
+       }
+-      D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
++      D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
++      D1(if (c->nextblock)
++         printk(KERN_DEBUG "Nextblock at  %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
+       if (!jeb->used_size) {
+               up(&c->alloc_sem);
+@@ -141,61 +222,215 @@
+       raw = jeb->gc_node;
+                       
+-      while(raw->flash_offset & 1) {
+-              D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
+-              jeb->gc_node = raw = raw->next_phys;
+-              if (!raw) {
++      while(ref_obsolete(raw)) {
++              D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
++              raw = raw->next_phys;
++              if (unlikely(!raw)) {
+                       printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
+                       printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", 
+                              jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+-                      spin_unlock_bh(&c->erase_completion_lock);
++                      jeb->gc_node = raw;
++                      spin_unlock(&c->erase_completion_lock);
+                       up(&c->alloc_sem);
+                       BUG();
+               }
+       }
+-      D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
++      jeb->gc_node = raw;
++
++      D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
++
+       if (!raw->next_in_ino) {
+               /* Inode-less node. Clean marker, snapshot or something like that */
+-              spin_unlock_bh(&c->erase_completion_lock);
++              /* FIXME: If it's something that needs to be copied, including something
++                 we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
++              spin_unlock(&c->erase_completion_lock);
+               jffs2_mark_node_obsolete(c, raw);
+               up(&c->alloc_sem);
+               goto eraseit_lock;
+       }
+                                                    
+-      inum = jffs2_raw_ref_to_inum(raw);
+-      D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
++      ic = jffs2_raw_ref_to_ic(raw);
+-      spin_unlock_bh(&c->erase_completion_lock);
++      /* We need to hold the inocache. Either the erase_completion_lock or
++         the inocache_lock are sufficient; we trade down since the inocache_lock 
++         causes less contention. */
++      spin_lock(&c->inocache_lock);
+-      D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, inum));
++      spin_unlock(&c->erase_completion_lock);
+-      inode = iget(OFNI_BS_2SFFJ(c), inum);
+-      if (is_bad_inode(inode)) {
+-              printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
+-              /* NB. This will happen again. We need to do something appropriate here. */
++      D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
++
++      /* Three possibilities:
++         1. Inode is already in-core. We must iget it and do proper
++            updating to its fragtree, etc.
++         2. Inode is not in-core, node is REF_PRISTINE. We lock the
++            inocache to prevent a read_inode(), copy the node intact.
++         3. Inode is not in-core, node is not pristine. We must iget()
++            and take the slow path.
++      */
++
++      switch(ic->state) {
++      case INO_STATE_CHECKEDABSENT:
++              /* It's been checked, but it's not currently in-core. 
++                 We can just copy any pristine nodes, but have
++                 to prevent anyone else from doing read_inode() while
++                 we're at it, so we set the state accordingly */
++              if (ref_flags(raw) == REF_PRISTINE)
++                      ic->state = INO_STATE_GC;
++              else {
++                      D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
++                                ic->ino));
++              }
++              break;
++
++      case INO_STATE_PRESENT:
++              /* It's in-core. GC must iget() it. */
++              break;
++
++      case INO_STATE_UNCHECKED:
++      case INO_STATE_CHECKING:
++      case INO_STATE_GC:
++              /* Should never happen. We should have finished checking
++                 by the time we actually start doing any GC, and since 
++                 we're holding the alloc_sem, no other garbage collection 
++                 can happen.
++              */
++              printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
++                     ic->ino, ic->state);
+               up(&c->alloc_sem);
+-              iput(inode);
+-              return -EIO;
++              spin_unlock(&c->inocache_lock);
++              BUG();
++
++      case INO_STATE_READING:
++              /* Someone's currently trying to read it. We must wait for
++                 them to finish and then go through the full iget() route
++                 to do the GC. However, sometimes read_inode() needs to get
++                 the alloc_sem() (for marking nodes invalid) so we must
++                 drop the alloc_sem before sleeping. */
++
++              up(&c->alloc_sem);
++              D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
++                        ic->ino, ic->state));
++              sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++              /* And because we dropped the alloc_sem we must start again from the 
++                 beginning. Ponder chance of livelock here -- we're returning success
++                 without actually making any progress.
++
++                 Q: What are the chances that the inode is back in INO_STATE_READING 
++                 again by the time we next enter this function? And that this happens
++                 enough times to cause a real delay?
++
++                 A: Small enough that I don't care :) 
++              */
++              return 0;
+       }
+-      f = JFFS2_INODE_INFO(inode);
++      /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
++         node intact, and we don't have to muck about with the fragtree etc. 
++         because we know it's not in-core. If it _was_ in-core, we go through
++         all the iget() crap anyway */
++
++      if (ic->state == INO_STATE_GC) {
++              spin_unlock(&c->inocache_lock);
++
++              ret = jffs2_garbage_collect_pristine(c, ic, raw);
++
++              spin_lock(&c->inocache_lock);
++              ic->state = INO_STATE_CHECKEDABSENT;
++              wake_up(&c->inocache_wq);
++
++              if (ret != -EBADFD) {
++                      spin_unlock(&c->inocache_lock);
++                      goto release_sem;
++              }
++
++              /* Fall through if it wanted us to, with inocache_lock held */
++      }
++
++      /* Prevent the fairly unlikely race where the gcblock is
++         entirely obsoleted by the final close of a file which had
++         the only valid nodes in the block, followed by erasure,
++         followed by freeing of the ic because the erased block(s)
++         held _all_ the nodes of that inode.... never been seen but
++         it's vaguely possible. */
++
++      inum = ic->ino;
++      nlink = ic->nlink;
++      spin_unlock(&c->inocache_lock);
++
++      f = jffs2_gc_fetch_inode(c, inum, nlink);
++      if (IS_ERR(f)) {
++              ret = PTR_ERR(f);
++              goto release_sem;
++      }
++      if (!f) {
++              ret = 0;
++              goto release_sem;
++      }
++
++      ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++
++      jffs2_gc_release_inode(c, f);
++
++ release_sem:
++      up(&c->alloc_sem);
++
++ eraseit_lock:
++      /* If we've finished this block, start it erasing */
++      spin_lock(&c->erase_completion_lock);
++
++ eraseit:
++      if (c->gcblock && !c->gcblock->used_size) {
++              D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
++              /* We're GC'ing an empty block? */
++              list_add_tail(&c->gcblock->list, &c->erase_pending_list);
++              c->gcblock = NULL;
++              c->nr_erasing_blocks++;
++              jffs2_erase_pending_trigger(c);
++      }
++      spin_unlock(&c->erase_completion_lock);
++
++      return ret;
++}
++
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
++                                    struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
++{
++      struct jffs2_node_frag *frag;
++      struct jffs2_full_dnode *fn = NULL;
++      struct jffs2_full_dirent *fd;
++      uint32_t start = 0, end = 0, nrfrags = 0;
++      int ret = 0;
++
+       down(&f->sem);
++
+       /* Now we have the lock for this inode. Check that it's still the one at the head
+          of the list. */
+-      if (raw->flash_offset & 1) {
++      spin_lock(&c->erase_completion_lock);
++
++      if (c->gcblock != jeb) {
++              spin_unlock(&c->erase_completion_lock);
++              D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
++              goto upnout;
++      }
++      if (ref_obsolete(raw)) {
++              spin_unlock(&c->erase_completion_lock);
+               D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
+               /* They'll call again */
+               goto upnout;
+       }
++      spin_unlock(&c->erase_completion_lock);
++
+       /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
+       if (f->metadata && f->metadata->raw == raw) {
+               fn = f->metadata;
+-              ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
++              ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
+               goto upnout;
+       }
+       
+-      for (frag = f->fraglist; frag; frag = frag->next) {
++      /* FIXME. Read node and do lookup? */
++      for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+               if (frag->node && frag->node->raw == raw) {
+                       fn = frag->node;
+                       end = frag->ofs + frag->size;
+@@ -206,13 +441,22 @@
+               }
+       }
+       if (fn) {
++              if (ref_flags(raw) == REF_PRISTINE) {
++                      ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
++                      if (!ret) {
++                              /* Urgh. Return it sensibly. */
++                              frag->node->raw = f->inocache->nodes;
++                      }       
++                      if (ret != -EBADFD)
++                              goto upnout;
++              }
+               /* We found a datanode. Do the GC */
+               if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
+                       /* It crosses a page boundary. Therefore, it must be a hole. */
+-                      ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
++                      ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
+               } else {
+                       /* It could still be a hole. But we GC the page this way anyway */
+-                      ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
++                      ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
+               }
+               goto upnout;
+       }
+@@ -224,12 +468,13 @@
+       }
+       if (fd && fd->ino) {
+-              ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
++              ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
+       } else if (fd) {
+-              ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
++              ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
+       } else {
+-              printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
+-              if (raw->flash_offset & 1) {
++              printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
++                     ref_offset(raw), f->inocache->ino);
++              if (ref_obsolete(raw)) {
+                       printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
+               } else {
+                       ret = -EIO;
+@@ -237,53 +482,207 @@
+       }
+  upnout:
+       up(&f->sem);
+-      up(&c->alloc_sem);
+-      iput(inode);
+- eraseit_lock:
+-      /* If we've finished this block, start it erasing */
+-      spin_lock_bh(&c->erase_completion_lock);
++      return ret;
++}
+- eraseit:
+-      if (c->gcblock && !c->gcblock->used_size) {
+-              D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+-              /* We're GC'ing an empty block? */
+-              list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+-              c->gcblock = NULL;
+-              c->nr_erasing_blocks++;
+-              jffs2_erase_pending_trigger(c);
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
++                                        struct jffs2_inode_cache *ic,
++                                        struct jffs2_raw_node_ref *raw)
++{
++      union jffs2_node_union *node;
++      struct jffs2_raw_node_ref *nraw;
++      size_t retlen;
++      int ret;
++      uint32_t phys_ofs, alloclen;
++      uint32_t crc, rawlen;
++      int retried = 0;
++
++      D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
++
++      rawlen = ref_totlen(c, c->gcblock, raw);
++
++      /* Ask for a small amount of space (or the totlen if smaller) because we
++         don't want to force wastage of the end of a block if splitting would
++         work. */
++      ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, 
++                                            rawlen), &phys_ofs, &alloclen);
++      if (ret)
++              return ret;
++
++      if (alloclen < rawlen) {
++              /* Doesn't fit untouched. We'll go the old route and split it */
++              return -EBADFD;
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
++      node = kmalloc(rawlen, GFP_KERNEL);
++      if (!node)
++               return -ENOMEM;
++
++      ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
++      if (!ret && retlen != rawlen)
++              ret = -EIO;
++      if (ret)
++              goto out_node;
++
++      crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
++      if (je32_to_cpu(node->u.hdr_crc) != crc) {
++              printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                     ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
++              goto bail;
++      }
++
++      switch(je16_to_cpu(node->u.nodetype)) {
++      case JFFS2_NODETYPE_INODE:
++              crc = crc32(0, node, sizeof(node->i)-8);
++              if (je32_to_cpu(node->i.node_crc) != crc) {
++                      printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                             ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
++                      goto bail;
++              }
++
++              if (je32_to_cpu(node->i.dsize)) {
++                      crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
++                      if (je32_to_cpu(node->i.data_crc) != crc) {
++                              printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                                     ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
++                              goto bail;
++                      }
++              }
++              break;
++
++      case JFFS2_NODETYPE_DIRENT:
++              crc = crc32(0, node, sizeof(node->d)-8);
++              if (je32_to_cpu(node->d.node_crc) != crc) {
++                      printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                             ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
++                      goto bail;
++              }
++
++              if (node->d.nsize) {
++                      crc = crc32(0, node->d.name, node->d.nsize);
++                      if (je32_to_cpu(node->d.name_crc) != crc) {
++                              printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                                     ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
++                              goto bail;
++                      }
++              }
++              break;
++      default:
++              printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", 
++                     ref_offset(raw), je16_to_cpu(node->u.nodetype));
++              goto bail;
++      }
++
++      nraw = jffs2_alloc_raw_node_ref();
++      if (!nraw) {
++              ret = -ENOMEM;
++              goto out_node;
++      }
++
++      /* OK, all the CRCs are good; this node can just be copied as-is. */
++ retry:
++      nraw->flash_offset = phys_ofs;
++      nraw->__totlen = rawlen;
++      nraw->next_phys = NULL;
++
++      ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
++
++      if (ret || (retlen != rawlen)) {
++              printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
++                       rawlen, phys_ofs, ret, retlen);
++              if (retlen) {
++                        /* Doesn't belong to any inode */
++                      nraw->next_in_ino = NULL;
++
++                      nraw->flash_offset |= REF_OBSOLETE;
++                      jffs2_add_physical_node_ref(c, nraw);
++                      jffs2_mark_node_obsolete(c, nraw);
++              } else {
++                      printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
++                        jffs2_free_raw_node_ref(nraw);
++              }
++              if (!retried && (nraw = jffs2_alloc_raw_node_ref())) {
++                      /* Try to reallocate space and retry */
++                      uint32_t dummy;
++                      struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
++
++                      retried = 1;
++
++                      D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
++                      
++                      ACCT_SANITY_CHECK(c,jeb);
++                      D1(ACCT_PARANOIA_CHECK(jeb));
++
++                      ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
++
++                      if (!ret) {
++                              D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
++
++                              ACCT_SANITY_CHECK(c,jeb);
++                              D1(ACCT_PARANOIA_CHECK(jeb));
++
++                              goto retry;
++                      }
++                      D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++                      jffs2_free_raw_node_ref(nraw);
++              }
++
++              jffs2_free_raw_node_ref(nraw);
++              if (!ret)
++                      ret = -EIO;
++              goto out_node;
++      }
++      nraw->flash_offset |= REF_PRISTINE;
++      jffs2_add_physical_node_ref(c, nraw);
++
++      /* Link into per-inode list. This is safe because of the ic
++         state being INO_STATE_GC. Note that if we're doing this
++         for an inode which is in-core, the 'nraw' pointer is then
++         going to be fetched from ic->nodes by our caller. */
++      spin_lock(&c->erase_completion_lock);
++        nraw->next_in_ino = ic->nodes;
++        ic->nodes = nraw;
++      spin_unlock(&c->erase_completion_lock);
++
++      jffs2_mark_node_obsolete(c, raw);
++      D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
++
++ out_node:
++      kfree(node);
+       return ret;
++ bail:
++      ret = -EBADFD;
++      goto out_node;
+ }
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dnode *fn)
++                                      struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_full_dnode *new_fn;
+       struct jffs2_raw_inode ri;
+-      unsigned short dev;
++      jint16_t dev;
+       char *mdata = NULL, mdatalen = 0;
+-      __u32 alloclen, phys_ofs;
++      uint32_t alloclen, phys_ofs;
+       int ret;
+-      if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++      if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
++          S_ISCHR(JFFS2_F_I_MODE(f)) ) {
+               /* For these, we don't actually need to read the old node */
+-              dev =  (MAJOR(to_kdev_t(inode->i_rdev)) << 8) | 
+-                      MINOR(to_kdev_t(inode->i_rdev));
++              /* FIXME: for minor or major > 255. */
++              dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | 
++                      JFFS2_F_I_RDEV_MIN(f)));
+               mdata = (char *)&dev;
+               mdatalen = sizeof(dev);
+               D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
+-      } else if (S_ISLNK(inode->i_mode)) {
++      } else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
+               mdatalen = fn->size;
+               mdata = kmalloc(fn->size, GFP_KERNEL);
+               if (!mdata) {
+                       printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+                       return -ENOMEM;
+               }
+-              ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
++              ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
+               if (ret) {
+                       printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
+                       kfree(mdata);
+@@ -295,34 +694,34 @@
+       
+       ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
+       if (ret) {
+-              printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
++              printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
+                      sizeof(ri)+ mdatalen, ret);
+               goto out;
+       }
+       
+       memset(&ri, 0, sizeof(ri));
+-      ri.magic = JFFS2_MAGIC_BITMASK;
+-      ri.nodetype = JFFS2_NODETYPE_INODE;
+-      ri.totlen = sizeof(ri) + mdatalen;
+-      ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
++      ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++      ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
++      ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+-      ri.ino = inode->i_ino;
+-      ri.version = ++f->highest_version;
+-      ri.mode = inode->i_mode;
+-      ri.uid = inode->i_uid;
+-      ri.gid = inode->i_gid;
+-      ri.isize = inode->i_size;
+-      ri.atime = inode->i_atime;
+-      ri.ctime = inode->i_ctime;
+-      ri.mtime = inode->i_mtime;
+-      ri.offset = 0;
+-      ri.csize = mdatalen;
+-      ri.dsize = mdatalen;
++      ri.ino = cpu_to_je32(f->inocache->ino);
++      ri.version = cpu_to_je32(++f->highest_version);
++      ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++      ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++      ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++      ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++      ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++      ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++      ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++      ri.offset = cpu_to_je32(0);
++      ri.csize = cpu_to_je32(mdatalen);
++      ri.dsize = cpu_to_je32(mdatalen);
+       ri.compr = JFFS2_COMPR_NONE;
+-      ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+-      ri.data_crc = crc32(0, mdata, mdatalen);
++      ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++      ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
+-      new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
++      new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
+       if (IS_ERR(new_fn)) {
+               printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -333,41 +732,40 @@
+       jffs2_free_full_dnode(fn);
+       f->metadata = new_fn;
+  out:
+-      if (S_ISLNK(inode->i_mode))
++      if (S_ISLNK(JFFS2_F_I_MODE(f)))
+               kfree(mdata);
+       return ret;
+ }
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dirent *fd)
++                                      struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_full_dirent *new_fd;
+       struct jffs2_raw_dirent rd;
+-      __u32 alloclen, phys_ofs;
++      uint32_t alloclen, phys_ofs;
+       int ret;
+-      rd.magic = JFFS2_MAGIC_BITMASK;
+-      rd.nodetype = JFFS2_NODETYPE_DIRENT;
++      rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+       rd.nsize = strlen(fd->name);
+-      rd.totlen = sizeof(rd) + rd.nsize;
+-      rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
++      rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
++      rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
+-      rd.pino = inode->i_ino;
+-      rd.version = ++f->highest_version;
+-      rd.ino = fd->ino;
+-      rd.mctime = max(inode->i_mtime, inode->i_ctime);
++      rd.pino = cpu_to_je32(f->inocache->ino);
++      rd.version = cpu_to_je32(++f->highest_version);
++      rd.ino = cpu_to_je32(fd->ino);
++      rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
+       rd.type = fd->type;
+-      rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
+-      rd.name_crc = crc32(0, fd->name, rd.nsize);
++      rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
++      rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
+       
+       ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
+       if (ret) {
+-              printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
++              printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
+                      sizeof(rd)+rd.nsize, ret);
+               return ret;
+       }
+-      new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
++      new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
+       if (IS_ERR(new_fd)) {
+               printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+@@ -378,19 +776,97 @@
+ }
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+-                                      struct inode *inode, struct jffs2_full_dirent *fd)
++                                      struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_full_dirent **fdp = &f->dents;
+       int found = 0;
+-      /* FIXME: When we run on NAND flash, we need to work out whether
+-         this deletion dirent is still needed to actively delete a
+-         'real' dirent with the same name that's still somewhere else
+-         on the flash. For now, we know that we've actually obliterated
+-         all the older dirents when they became obsolete, so we didn't
+-         really need to write the deletion to flash in the first place.
+-      */
++      /* On a medium where we can't actually mark nodes obsolete
++         pernamently, such as NAND flash, we need to work out
++         whether this deletion dirent is still needed to actively
++         delete a 'real' dirent with the same name that's still
++         somewhere else on the flash. */
++      if (!jffs2_can_mark_obsolete(c)) {
++              struct jffs2_raw_dirent *rd;
++              struct jffs2_raw_node_ref *raw;
++              int ret;
++              size_t retlen;
++              int name_len = strlen(fd->name);
++              uint32_t name_crc = crc32(0, fd->name, name_len);
++              uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
++
++              rd = kmalloc(rawlen, GFP_KERNEL);
++              if (!rd)
++                      return -ENOMEM;
++
++              /* Prevent the erase code from nicking the obsolete node refs while
++                 we're looking at them. I really don't like this extra lock but
++                 can't see any alternative. Suggestions on a postcard to... */
++              down(&c->erase_free_sem);
++
++              for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
++
++                      /* We only care about obsolete ones */
++                      if (!(ref_obsolete(raw)))
++                              continue;
++
++                      /* Any dirent with the same name is going to have the same length... */
++                      if (ref_totlen(c, NULL, raw) != rawlen)
++                              continue;
++
++                      /* Doesn't matter if there's one in the same erase block. We're going to 
++                         delete it too at the same time. */
++                      if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
++                              continue;
++
++                      D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
++
++                      /* This is an obsolete node belonging to the same directory, and it's of the right
++                         length. We need to take a closer look...*/
++                      ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
++                      if (ret) {
++                              printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
++                              /* If we can't read it, we don't need to continue to obsolete it. Continue */
++                              continue;
++                      }
++                      if (retlen != rawlen) {
++                              printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n",
++                                     retlen, rawlen, ref_offset(raw));
++                              continue;
++                      }
++
++                      if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
++                              continue;
++
++                      /* If the name CRC doesn't match, skip */
++                      if (je32_to_cpu(rd->name_crc) != name_crc)
++                              continue;
++
++                      /* If the name length doesn't match, or it's another deletion dirent, skip */
++                      if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
++                              continue;
++
++                      /* OK, check the actual name now */
++                      if (memcmp(rd->name, fd->name, name_len))
++                              continue;
++
++                      /* OK. The name really does match. There really is still an older node on
++                         the flash which our deletion dirent obsoletes. So we have to write out
++                         a new deletion dirent to replace it */
++                      up(&c->erase_free_sem);
++
++                      D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
++                                ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
++                      kfree(rd);
++
++                      return jffs2_garbage_collect_dirent(c, jeb, f, fd);
++              }
++
++              up(&c->erase_free_sem);
++              kfree(rd);
++      }
++
++      /* No need for it any more. Just mark it obsolete and remove it from the list */
+       while (*fdp) {
+               if ((*fdp) == fd) {
+                       found = 1;
+@@ -400,7 +876,7 @@
+               fdp = &(*fdp)->next;
+       }
+       if (!found) {
+-              printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
++              printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
+       }
+       jffs2_mark_node_obsolete(c, fd->raw);
+       jffs2_free_full_dirent(fd);
+@@ -408,93 +884,95 @@
+ }
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+-                                    struct inode *inode, struct jffs2_full_dnode *fn,
+-                                    __u32 start, __u32 end)
++                                    struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++                                    uint32_t start, uint32_t end)
+ {
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_raw_inode ri;
+       struct jffs2_node_frag *frag;
+       struct jffs2_full_dnode *new_fn;
+-      __u32 alloclen, phys_ofs;
++      uint32_t alloclen, phys_ofs;
+       int ret;
+-      D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
+-                inode->i_ino, start, end));
++      D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
++                f->inocache->ino, start, end));
+       
+       memset(&ri, 0, sizeof(ri));
+       if(fn->frags > 1) {
+               size_t readlen;
+-              __u32 crc;
++              uint32_t crc;
+               /* It's partially obsoleted by a later write. So we have to 
+                  write it out again with the _same_ version as before */
+-              ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
++              ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
+               if (readlen != sizeof(ri) || ret) {
+-                      printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
++                      printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
+                       goto fill;
+               }
+-              if (ri.nodetype != JFFS2_NODETYPE_INODE) {
++              if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
+                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+-                             fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
++                             ref_offset(fn->raw),
++                             je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
+                       return -EIO;
+               }
+-              if (ri.totlen != sizeof(ri)) {
+-                      printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
+-                             fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
++              if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
++                      printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
++                             ref_offset(fn->raw),
++                             je32_to_cpu(ri.totlen), sizeof(ri));
+                       return -EIO;
+               }
+               crc = crc32(0, &ri, sizeof(ri)-8);
+-              if (crc != ri.node_crc) {
++              if (crc != je32_to_cpu(ri.node_crc)) {
+                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+-                             fn->raw->flash_offset & ~3, ri.node_crc, crc);
++                             ref_offset(fn->raw), 
++                             je32_to_cpu(ri.node_crc), crc);
+                       /* FIXME: We could possibly deal with this by writing new holes for each frag */
+-                      printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", 
+-                             start, end, inode->i_ino);
++                      printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
++                             start, end, f->inocache->ino);
+                       goto fill;
+               }
+               if (ri.compr != JFFS2_COMPR_ZERO) {
+-                      printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
+-                      printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", 
+-                             start, end, inode->i_ino);
++                      printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
++                      printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
++                             start, end, f->inocache->ino);
+                       goto fill;
+               }
+       } else {
+       fill:
+-              ri.magic = JFFS2_MAGIC_BITMASK;
+-              ri.nodetype = JFFS2_NODETYPE_INODE;
+-              ri.totlen = sizeof(ri);
+-              ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
++              ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++              ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++              ri.totlen = cpu_to_je32(sizeof(ri));
++              ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+-              ri.ino = inode->i_ino;
+-              ri.version = ++f->highest_version;
+-              ri.offset = start;
+-              ri.dsize = end - start;
+-              ri.csize = 0;
++              ri.ino = cpu_to_je32(f->inocache->ino);
++              ri.version = cpu_to_je32(++f->highest_version);
++              ri.offset = cpu_to_je32(start);
++              ri.dsize = cpu_to_je32(end - start);
++              ri.csize = cpu_to_je32(0);
+               ri.compr = JFFS2_COMPR_ZERO;
+       }
+-      ri.mode = inode->i_mode;
+-      ri.uid = inode->i_uid;
+-      ri.gid = inode->i_gid;
+-      ri.isize = inode->i_size;
+-      ri.atime = inode->i_atime;
+-      ri.ctime = inode->i_ctime;
+-      ri.mtime = inode->i_mtime;
+-      ri.data_crc = 0;
+-      ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
++      ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++      ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++      ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++      ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++      ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++      ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++      ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++      ri.data_crc = cpu_to_je32(0);
++      ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+       ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
+       if (ret) {
+-              printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
++              printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
+                      sizeof(ri), ret);
+               return ret;
+       }
+-      new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
++      new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
+       if (IS_ERR(new_fn)) {
+               printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+               return PTR_ERR(new_fn);
+       }
+-      if (ri.version == f->highest_version) {
++      if (je32_to_cpu(ri.version) == f->highest_version) {
+               jffs2_add_full_dnode_to_inode(c, f, new_fn);
+               if (f->metadata) {
+                       jffs2_mark_node_obsolete(c, f->metadata->raw);
+@@ -510,12 +988,17 @@
+        * number as before. (Except in case of error -- see 'goto fill;' 
+        * above.)
+        */
+-      D1(if(fn->frags <= 1) {
++      D1(if(unlikely(fn->frags <= 1)) {
+               printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+-                     fn->frags, ri.version, f->highest_version, ri.ino);
++                     fn->frags, je32_to_cpu(ri.version), f->highest_version,
++                     je32_to_cpu(ri.ino));
+       });
+-      for (frag = f->fraglist; frag; frag = frag->next) {
++      /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
++      mark_ref_normal(new_fn->raw);
++
++      for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); 
++           frag; frag = frag_next(frag)) {
+               if (frag->ofs > fn->size + fn->ofs)
+                       break;
+               if (frag->node == fn) {
+@@ -540,49 +1023,146 @@
+ }
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+-                                     struct inode *inode, struct jffs2_full_dnode *fn,
+-                                     __u32 start, __u32 end)
++                                     struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++                                     uint32_t start, uint32_t end)
+ {
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_full_dnode *new_fn;
+       struct jffs2_raw_inode ri;
+-      __u32 alloclen, phys_ofs, offset, orig_end;     
++      uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;      
+       int ret = 0;
+       unsigned char *comprbuf = NULL, *writebuf;
+-      struct page *pg;
++      unsigned long pg;
+       unsigned char *pg_ptr;
+-
+       memset(&ri, 0, sizeof(ri));
+-      D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
+-                inode->i_ino, start, end));
++      D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
++                f->inocache->ino, start, end));
+       orig_end = end;
++      orig_start = start;
++      if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
++              /* Attempt to do some merging. But only expand to cover logically
++                 adjacent frags if the block containing them is already considered
++                 to be dirty. Otherwise we end up with GC just going round in 
++                 circles dirtying the nodes it already wrote out, especially 
++                 on NAND where we have small eraseblocks and hence a much higher
++                 chance of nodes having to be split to cross boundaries. */
+-      /* If we're looking at the last node in the block we're
+-         garbage-collecting, we allow ourselves to merge as if the
+-         block was already erasing. We're likely to be GC'ing a
+-         partial page, and the next block we GC is likely to have
+-         the other half of this page right at the beginning, which
+-         means we'd expand it _then_, as nr_erasing_blocks would have
+-         increased since we checked, and in doing so would obsolete 
+-         the partial node which we'd have written here. Meaning that 
+-         the GC would churn and churn, and just leave dirty blocks in
+-         it's wake.
+-      */
+-      if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
+-              /* Shitloads of space */
+-              /* FIXME: Integrate this properly with GC calculations */
+-              start &= ~(PAGE_CACHE_SIZE-1);
+-              end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
+-              D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
+-                        start, end));
+-              if (end < orig_end) {
+-                      printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
+-                      end = orig_end;
++              struct jffs2_node_frag *frag;
++              uint32_t min, max;
++
++              min = start & ~(PAGE_CACHE_SIZE-1);
++              max = min + PAGE_CACHE_SIZE;
++
++              frag = jffs2_lookup_node_frag(&f->fragtree, start);
++
++              /* BUG_ON(!frag) but that'll happen anyway... */
++
++              BUG_ON(frag->ofs != start);
++
++              /* First grow down... */
++              while((frag = frag_prev(frag)) && frag->ofs >= min) {
++
++                      /* If the previous frag doesn't even reach the beginning, there's
++                         excessive fragmentation. Just merge. */
++                      if (frag->ofs > min) {
++                              D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
++                                        frag->ofs, frag->ofs+frag->size));
++                              start = frag->ofs;
++                              continue;
++                      }
++                      /* OK. This frag holds the first byte of the page. */
++                      if (!frag->node || !frag->node->raw) {
++                              D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
++                                        frag->ofs, frag->ofs+frag->size));
++                              break;
++                      } else {
++
++                              /* OK, it's a frag which extends to the beginning of the page. Does it live 
++                                 in a block which is still considered clean? If so, don't obsolete it.
++                                 If not, cover it anyway. */
++
++                              struct jffs2_raw_node_ref *raw = frag->node->raw;
++                              struct jffs2_eraseblock *jeb;
++
++                              jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++                              if (jeb == c->gcblock) {
++                                      D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++                                      start = frag->ofs;
++                                      break;
+               }
++                              if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++                                      D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, jeb->offset));
++                                      break;
++                              }
++
++                              D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, jeb->offset));
++                              start = frag->ofs;
++                              break;
++                      }
++              }
++
++              /* ... then up */
++
++              /* Find last frag which is actually part of the node we're to GC. */
++              frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
++
++              while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
++
++                      /* If the previous frag doesn't even reach the beginning, there's lots
++                         of fragmentation. Just merge. */
++                      if (frag->ofs+frag->size < max) {
++                              D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
++                                        frag->ofs, frag->ofs+frag->size));
++                              end = frag->ofs + frag->size;
++                              continue;
++                      }
++
++                      if (!frag->node || !frag->node->raw) {
++                              D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
++                                        frag->ofs, frag->ofs+frag->size));
++                              break;
++                      } else {
++
++                              /* OK, it's a frag which extends to the beginning of the page. Does it live 
++                                 in a block which is still considered clean? If so, don't obsolete it.
++                                 If not, cover it anyway. */
++
++                              struct jffs2_raw_node_ref *raw = frag->node->raw;
++                              struct jffs2_eraseblock *jeb;
++
++                              jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++                              if (jeb == c->gcblock) {
++                                      D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++                                      end = frag->ofs + frag->size;
++                                      break;
++                              }
++                              if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++                                      D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, jeb->offset));
++                                      break;
++                              }
++
++                              D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
++                                                frag->ofs, frag->ofs+frag->size, jeb->offset));
++                              end = frag->ofs + frag->size;
++                              break;
++                      }
++              }
++              D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", 
++                        orig_start, orig_end, start, end));
++
++              BUG_ON(end > JFFS2_F_I_SIZE(f));
++              BUG_ON(end < orig_end);
++              BUG_ON(start > orig_start);
+       }
+       
+       /* First, use readpage() to read the appropriate page into the page cache */
+@@ -592,63 +1172,58 @@
+        *    page OK. We'll actually write it out again in commit_write, which is a little
+        *    suboptimal, but at least we're correct.
+        */
+-      pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
++      pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+-      if (IS_ERR(pg)) {
+-              printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
+-              return PTR_ERR(pg);
++      if (IS_ERR(pg_ptr)) {
++              printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
++              return PTR_ERR(pg_ptr);
+       }
+-      pg_ptr = (char *)kmap(pg);
+-      comprbuf = kmalloc(end - start, GFP_KERNEL);
+       offset = start;
+       while(offset < orig_end) {
+-              __u32 datalen;
+-              __u32 cdatalen;
+-              char comprtype = JFFS2_COMPR_NONE;
++              uint32_t datalen;
++              uint32_t cdatalen;
++              uint16_t comprtype = JFFS2_COMPR_NONE;
+               ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+               if (ret) {
+-                      printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
++                      printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
+                              sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
+                       break;
+               }
+-              cdatalen = min(alloclen - sizeof(ri), end - offset);
++              cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
+               datalen = end - offset;
+               writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
+-              if (comprbuf) {
+-                      comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
+-              }
+-              if (comprtype) {
+-                      writebuf = comprbuf;
+-              } else {
+-                      datalen = cdatalen;
+-              }
+-              ri.magic = JFFS2_MAGIC_BITMASK;
+-              ri.nodetype = JFFS2_NODETYPE_INODE;
+-              ri.totlen = sizeof(ri) + cdatalen;
+-              ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
++              comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
+-              ri.ino = inode->i_ino;
+-              ri.version = ++f->highest_version;
+-              ri.mode = inode->i_mode;
+-              ri.uid = inode->i_uid;
+-              ri.gid = inode->i_gid;
+-              ri.isize = inode->i_size;
+-              ri.atime = inode->i_atime;
+-              ri.ctime = inode->i_ctime;
+-              ri.mtime = inode->i_mtime;
+-              ri.offset = offset;
+-              ri.csize = cdatalen;
+-              ri.dsize = datalen;
+-              ri.compr = comprtype;
+-              ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+-              ri.data_crc = crc32(0, writebuf, cdatalen);
++              ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++              ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++              ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
++              ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+       
+-              new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
++              ri.ino = cpu_to_je32(f->inocache->ino);
++              ri.version = cpu_to_je32(++f->highest_version);
++              ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++              ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++              ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++              ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++              ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++              ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++              ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++              ri.offset = cpu_to_je32(offset);
++              ri.csize = cpu_to_je32(cdatalen);
++              ri.dsize = cpu_to_je32(datalen);
++              ri.compr = comprtype & 0xff;
++              ri.usercompr = (comprtype >> 8) & 0xff;
++              ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++              ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++      
++              new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
++
++              jffs2_free_comprbuf(comprbuf, writebuf);
+               if (IS_ERR(new_fn)) {
+                       printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -663,12 +1238,8 @@
+                       f->metadata = NULL;
+               }
+       }
+-      if (comprbuf) kfree(comprbuf);
+-      kunmap(pg);
+-      /* XXX: Does the page get freed automatically? */
+-      /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
+-      page_cache_release(pg);
++      jffs2_gc_release_page(c, pg_ptr, &pg);
+       return ret;
+ }
+--- linux-2.4.21/fs/jffs2/ioctl.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/ioctl.c
+@@ -1,37 +1,13 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+@@ -42,6 +18,6 @@
+ {
+       /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+          will include compression support etc. */
+-      return -EINVAL;
++      return -ENOTTY;
+ }
+       
+--- linux-2.4.21/fs/jffs2/malloc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/malloc.c
+@@ -1,37 +1,13 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+@@ -47,6 +23,9 @@
+ #define JFFS2_SLAB_POISON 0
+ #endif
++// replace this by #define D3 (x) x for cache debugging
++#define D3(x)
++
+ /* These are initialised to NULL in the kernel startup code.
+    If you're porting to other operating systems, beware */
+ static kmem_cache_t *full_dnode_slab;
+@@ -57,57 +36,47 @@
+ static kmem_cache_t *node_frag_slab;
+ static kmem_cache_t *inode_cache_slab;
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+-{
+-      struct jffs2_tmp_dnode_info *next;
+-
+-      while (tn) {
+-              next = tn;
+-              tn = tn->next;
+-              jffs2_free_full_dnode(next->fn);
+-              jffs2_free_tmp_dnode_info(next);
+-      }
+-}
+-
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+-{
+-      struct jffs2_full_dirent *next;
+-
+-      while (fd) {
+-              next = fd->next;
+-              jffs2_free_full_dirent(fd);
+-              fd = next;
+-      }
+-}
+-
+ int __init jffs2_create_slab_caches(void)
+ {
+-      full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      full_dnode_slab = kmem_cache_create("jffs2_full_dnode", 
++                                          sizeof(struct jffs2_full_dnode),
++                                          0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!full_dnode_slab)
+               goto err;
+-      raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
++                                          sizeof(struct jffs2_raw_dirent),
++                                          0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!raw_dirent_slab)
+               goto err;
+-      raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
++                                         sizeof(struct jffs2_raw_inode),
++                                         0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!raw_inode_slab)
+               goto err;
+-      tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
++                                              sizeof(struct jffs2_tmp_dnode_info),
++                                              0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!tmp_dnode_info_slab)
+               goto err;
+-      raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
++                                            sizeof(struct jffs2_raw_node_ref),
++                                            0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!raw_node_ref_slab)
+               goto err;
+-      node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
++      node_frag_slab = kmem_cache_create("jffs2_node_frag",
++                                         sizeof(struct jffs2_node_frag),
++                                         0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (!node_frag_slab)
+               goto err;
+-      inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
+-
++      inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
++                                           sizeof(struct jffs2_inode_cache),
++                                           0, JFFS2_SLAB_POISON, NULL, NULL);
+       if (inode_cache_slab)
+               return 0;
+  err:
+@@ -131,7 +100,6 @@
+               kmem_cache_destroy(node_frag_slab);
+       if(inode_cache_slab)
+               kmem_cache_destroy(inode_cache_slab);
+-
+ }
+ struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+@@ -146,75 +114,92 @@
+ struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
+ {
+-      void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++      struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
+       return ret;
+ }
+ void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
+ {
++      D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
+       kmem_cache_free(full_dnode_slab, x);
+ }
+ struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
+ {
+-      return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++      struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
++      return ret;
+ }
+ void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
+ {
++      D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
+       kmem_cache_free(raw_dirent_slab, x);
+ }
+ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
+ {
+-      return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++      struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
++      return ret;
+ }
+ void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
+ {
++      D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
+       kmem_cache_free(raw_inode_slab, x);
+ }
+ struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
+ {
+-      return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++      struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
++      return ret;
+ }
+ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
+ {
++      D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
+       kmem_cache_free(tmp_dnode_info_slab, x);
+ }
+ struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
+ {
+-      return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++      struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
++      return ret;
+ }
+ void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
+ {
++      D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
+       kmem_cache_free(raw_node_ref_slab, x);
+ }
+ struct jffs2_node_frag *jffs2_alloc_node_frag(void)
+ {
+-      return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++      struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++      D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
++      return ret;
+ }
+ void jffs2_free_node_frag(struct jffs2_node_frag *x)
+ {
++      D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
+       kmem_cache_free(node_frag_slab, x);
+ }
+ struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
+ {
+       struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
+-      D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
++      D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+       return ret;
+ }
+ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
+ {
+-      D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
++      D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+       kmem_cache_free(inode_cache_slab, x);
+ }
+--- linux-2.4.21/fs/jffs2/nodelist.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.c
+@@ -1,44 +1,24 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/rbtree.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include "nodelist.h"
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
+@@ -78,7 +58,7 @@
+ /* Put a new tmp_dnode_info into the list, keeping the list in 
+    order of increasing version
+ */
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+ {
+       struct jffs2_tmp_dnode_info **prev = list;
+       
+@@ -89,93 +69,156 @@
+         *prev = tn;
+ }
++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
++{
++      struct jffs2_tmp_dnode_info *next;
++
++      while (tn) {
++              next = tn;
++              tn = tn->next;
++              jffs2_free_full_dnode(next->fn);
++              jffs2_free_tmp_dnode_info(next);
++      }
++}
++
++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++{
++      struct jffs2_full_dirent *next;
++
++      while (fd) {
++              next = fd->next;
++              jffs2_free_full_dirent(fd);
++              fd = next;
++      }
++}
++
++/* Returns first valid node after 'ref'. May return 'ref' */
++static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
++{
++      while (ref && ref->next_in_ino) {
++              if (!ref_obsolete(ref))
++                      return ref;
++              D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
++              ref = ref->next_in_ino;
++      }
++      return NULL;
++}
++
+ /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+    with this ino, returning the former in order of version */
+-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                         struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+-                        __u32 *highest_version, __u32 *latest_mctime,
+-                        __u32 *mctime_ver)
++                        uint32_t *highest_version, uint32_t *latest_mctime,
++                        uint32_t *mctime_ver)
+ {
+-      struct jffs2_raw_node_ref *ref = f->inocache->nodes;
++      struct jffs2_raw_node_ref *ref, *valid_ref;
+       struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+       struct jffs2_full_dirent *fd, *ret_fd = NULL;
+-
+       union jffs2_node_union node;
+       size_t retlen;
+       int err;
+       *mctime_ver = 0;
+-      D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
+-      if (!f->inocache->nodes) {
+-              printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
+-      }
+-      for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
+-              /* Work out whether it's a data node or a dirent node */
+-              if (ref->flash_offset & 1) {
+-                      /* FIXME: On NAND flash we may need to read these */
+-                      D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
+-                      continue;
+-              }
+-              err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
++      D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
++
++      spin_lock(&c->erase_completion_lock);
++
++      valid_ref = jffs2_first_valid_node(f->inocache->nodes);
++
++      if (!valid_ref && (f->inocache->ino != 1))
++              printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
++
++      while (valid_ref) {
++              /* We can hold a pointer to a non-obsolete node without the spinlock,
++                 but _obsolete_ nodes may disappear at any time, if the block
++                 they're in gets erased. So if we mark 'ref' obsolete while we're
++                 not holding the lock, it can go away immediately. For that reason,
++                 we find the next valid node first, before processing 'ref'.
++              */
++              ref = valid_ref;
++              valid_ref = jffs2_first_valid_node(ref->next_in_ino);
++              spin_unlock(&c->erase_completion_lock);
++
++              cond_resched();
++
++              /* FIXME: point() */
++              err = jffs2_flash_read(c, (ref_offset(ref)), 
++                                     min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
++                                     &retlen, (void *)&node);
+               if (err) {
+-                      printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
++                      printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
+                       goto free_out;
+               }
+                       
+                       /* Check we've managed to read at least the common node header */
+-              if (retlen < min(ref->totlen, sizeof(node.u))) {
++              if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
+                       printk(KERN_WARNING "short read in get_inode_nodes()\n");
+                       err = -EIO;
+                       goto free_out;
+               }
+                       
+-              switch (node.u.nodetype) {
++              switch (je16_to_cpu(node.u.nodetype)) {
+               case JFFS2_NODETYPE_DIRENT:
+-                      D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
++                      D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
++                      if (ref_flags(ref) == REF_UNCHECKED) {
++                              printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
++                              BUG();
++                      }
+                       if (retlen < sizeof(node.d)) {
+                               printk(KERN_WARNING "short read in get_inode_nodes()\n");
+                               err = -EIO;
+                               goto free_out;
+                       }
+-                      if (node.d.version > *highest_version)
+-                              *highest_version = node.d.version;
+-                      if (ref->flash_offset & 1) {
+-                              /* Obsoleted */
++                      /* sanity check */
++                      if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
++                              printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
++                                     ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
++                              jffs2_mark_node_obsolete(c, ref);
++                              spin_lock(&c->erase_completion_lock);
+                               continue;
+                       }
++                      if (je32_to_cpu(node.d.version) > *highest_version)
++                              *highest_version = je32_to_cpu(node.d.version);
++                      if (ref_obsolete(ref)) {
++                              /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++                              printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
++                                     ref_offset(ref));
++                              BUG();
++                      }
++                      
+                       fd = jffs2_alloc_full_dirent(node.d.nsize+1);
+                       if (!fd) {
+                               err = -ENOMEM;
+                               goto free_out;
+                       }
+-                      memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
+                       fd->raw = ref;
+-                      fd->version = node.d.version;
+-                      fd->ino = node.d.ino;
++                      fd->version = je32_to_cpu(node.d.version);
++                      fd->ino = je32_to_cpu(node.d.ino);
+                       fd->type = node.d.type;
+                       /* Pick out the mctime of the latest dirent */
+                       if(fd->version > *mctime_ver) {
+                               *mctime_ver = fd->version;
+-                              *latest_mctime = node.d.mctime;
++                              *latest_mctime = je32_to_cpu(node.d.mctime);
+                       }
+                       /* memcpy as much of the name as possible from the raw
+                          dirent we've already read from the flash
+                       */
+                       if (retlen > sizeof(struct jffs2_raw_dirent))
+-                              memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
++                              memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+                               
+                       /* Do we need to copy any more of the name directly
+                          from the flash?
+                       */
+                       if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
++                              /* FIXME: point() */
+                               int already = retlen - sizeof(struct jffs2_raw_dirent);
+                                       
+-                              err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen, 
++                              err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, 
+                                                  node.d.nsize - already, &retlen, &fd->name[already]);
+                               if (!err && retlen != node.d.nsize - already)
+                                       err = -EIO;
+@@ -188,6 +231,7 @@
+                       }
+                       fd->nhash = full_name_hash(fd->name, node.d.nsize);
+                       fd->next = NULL;
++                      fd->name[node.d.nsize] = '\0';
+                               /* Wheee. We now have a complete jffs2_full_dirent structure, with
+                                  the name in it and everything. Link it into the list 
+                               */
+@@ -196,21 +240,126 @@
+                       break;
+               case JFFS2_NODETYPE_INODE:
+-                      D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
++                      D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
+                       if (retlen < sizeof(node.i)) {
+                               printk(KERN_WARNING "read too short for dnode\n");
+                               err = -EIO;
+                               goto free_out;
+                       }
+-                      if (node.i.version > *highest_version)
+-                              *highest_version = node.i.version;
+-                      D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version));
++                      if (je32_to_cpu(node.i.version) > *highest_version)
++                              *highest_version = je32_to_cpu(node.i.version);
++                      D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
+-                      if (ref->flash_offset & 1) {
+-                              D1(printk(KERN_DEBUG "obsoleted\n"));
+-                              /* Obsoleted */
++                      if (ref_obsolete(ref)) {
++                              /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++                              printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
++                                     ref_offset(ref));
++                              BUG();
++                      }
++
++                      /* If we've never checked the CRCs on this node, check them now. */
++                      if (ref_flags(ref) == REF_UNCHECKED) {
++                              uint32_t crc, len;
++                              struct jffs2_eraseblock *jeb;
++
++                              crc = crc32(0, &node, sizeof(node.i)-8);
++                              if (crc != je32_to_cpu(node.i.node_crc)) {
++                                      printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                                             ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
++                                      jffs2_mark_node_obsolete(c, ref);
++                                      spin_lock(&c->erase_completion_lock);
++                                      continue;
++                              }
++                              
++                              /* sanity checks */
++                              if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
++                                   PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
++                                      printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino  %d, version %d, isize %d, csize %d, dsize %d \n",
++                                              ref_offset(ref),  je32_to_cpu(node.i.totlen),  je32_to_cpu(node.i.ino),
++                                              je32_to_cpu(node.i.version),  je32_to_cpu(node.i.isize), 
++                                              je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
++                                      jffs2_mark_node_obsolete(c, ref);
++                                      spin_lock(&c->erase_completion_lock);
++                                      continue;
++                              }
++
++                              if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
++                                      unsigned char *buf=NULL;
++                                      uint32_t pointed = 0;
++#ifndef __ECOS
++                                      if (c->mtd->point) {
++                                              err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++                                                                   &retlen, &buf);
++                                              if (!err && retlen < je32_to_cpu(node.i.csize)) {
++                                                      D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
++                                                      c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++                                              } else if (err){
++                                                      D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
++                                              } else
++                                                      pointed = 1; /* succefully pointed to device */
++                                      }
++#endif                                        
++                                      if(!pointed){
++                                              buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
++                                              if (!buf)
++                                                      return -ENOMEM;
++                                              
++                                              err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++                                                                     &retlen, buf);
++                                              if (!err && retlen != je32_to_cpu(node.i.csize))
++                                                      err = -EIO;
++                                              if (err) {
++                                                      kfree(buf);
++                                                      return err;
++                                              }
++                                      }
++                                      crc = crc32(0, buf, je32_to_cpu(node.i.csize));
++                                      if(!pointed)
++                                              kfree(buf);
++#ifndef __ECOS
++                                      else
++                                              c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++#endif
++
++                                      if (crc != je32_to_cpu(node.i.data_crc)) {
++                                              printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                                                     ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
++                                              jffs2_mark_node_obsolete(c, ref);
++                                              spin_lock(&c->erase_completion_lock);
+                               continue;
+                       }
++                                      
++                              }
++
++                              /* Mark the node as having been checked and fix the accounting accordingly */
++                              spin_lock(&c->erase_completion_lock);
++                              jeb = &c->blocks[ref->flash_offset / c->sector_size];
++                              len = ref_totlen(c, jeb, ref);
++
++                              jeb->used_size += len;
++                              jeb->unchecked_size -= len;
++                              c->used_size += len;
++                              c->unchecked_size -= len;
++
++                              /* If node covers at least a whole page, or if it starts at the 
++                                 beginning of a page and runs to the end of the file, or if 
++                                 it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
++
++                                 If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
++                                 when the overlapping node(s) get added to the tree anyway. 
++                              */
++                              if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
++                                  ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
++                                    (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) ==  je32_to_cpu(node.i.isize)))) {
++                                      D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
++                                      ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
++                              } else {
++                                      D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
++                                      ref->flash_offset = ref_offset(ref) | REF_NORMAL;
++                              }
++                              spin_unlock(&c->erase_completion_lock);
++                      }
++
+                       tn = jffs2_alloc_tmp_dnode_info();
+                       if (!tn) {
+                               D1(printk(KERN_DEBUG "alloc tn failed\n"));
+@@ -225,36 +374,76 @@
+                               jffs2_free_tmp_dnode_info(tn);
+                               goto free_out;
+                       }
+-                      tn->version = node.i.version;
+-                      tn->fn->ofs = node.i.offset;
++                      tn->version = je32_to_cpu(node.i.version);
++                      tn->fn->ofs = je32_to_cpu(node.i.offset);
+                       /* There was a bug where we wrote hole nodes out with
+                          csize/dsize swapped. Deal with it */
+-                      if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize)
+-                              tn->fn->size = node.i.csize;
++                      if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
++                              tn->fn->size = je32_to_cpu(node.i.csize);
+                       else // normal case...
+-                              tn->fn->size = node.i.dsize;
++                              tn->fn->size = je32_to_cpu(node.i.dsize);
+                       tn->fn->raw = ref;
+-                      D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
++                      D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
++                                ref_offset(ref), je32_to_cpu(node.i.version),
++                                je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
+                       jffs2_add_tn_to_list(tn, &ret_tn);
+                       break;
+               default:
+-                      switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
++                      if (ref_flags(ref) == REF_UNCHECKED) {
++                              struct jffs2_eraseblock *jeb;
++                              uint32_t len;
++
++                              printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
++                                     je16_to_cpu(node.u.nodetype), ref_offset(ref));
++
++                              /* Mark the node as having been checked and fix the accounting accordingly */
++                              spin_lock(&c->erase_completion_lock);
++                              jeb = &c->blocks[ref->flash_offset / c->sector_size];
++                              len = ref_totlen(c, jeb, ref);
++
++                              jeb->used_size += len;
++                              jeb->unchecked_size -= len;
++                              c->used_size += len;
++                              c->unchecked_size -= len;
++
++                              mark_ref_normal(ref);
++                              spin_unlock(&c->erase_completion_lock);
++                      }
++                      node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
++                      if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
++                              /* Hmmm. This should have been caught at scan time. */
++                              printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
++                                     ref_offset(ref));
++                              printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", 
++                                     je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
++                                     je32_to_cpu(node.u.hdr_crc));
++                              jffs2_mark_node_obsolete(c, ref);
++                      } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
+                       case JFFS2_FEATURE_INCOMPAT:
+-                              printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++                              printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++                              /* EEP */
++                              BUG();
+                               break;
+                       case JFFS2_FEATURE_ROCOMPAT:
+-                              printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++                              printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++                              if (!(c->flags & JFFS2_SB_FLAG_RO))
++                                      BUG();
+                               break;
+                       case JFFS2_FEATURE_RWCOMPAT_COPY:
+-                              printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++                              printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+                               break;
+                       case JFFS2_FEATURE_RWCOMPAT_DELETE:
+-                              printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++                              printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++                              jffs2_mark_node_obsolete(c, ref);
+                               break;
+                       }
++
+               }
++              spin_lock(&c->erase_completion_lock);
++
+       }
++      spin_unlock(&c->erase_completion_lock);
+       *tnp = ret_tn;
+       *fdp = ret_fd;
+@@ -266,19 +455,30 @@
+       return err;
+ }
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
++{
++      spin_lock(&c->inocache_lock);
++      ic->state = state;
++      wake_up(&c->inocache_wq);
++      spin_unlock(&c->inocache_lock);
++}
++
++/* During mount, this needs no locking. During normal operation, its
++   callers want to do other stuff while still holding the inocache_lock.
++   Rather than introducing special case get_ino_cache functions or 
++   callbacks, we just let the caller do the locking itself. */
++   
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+       struct jffs2_inode_cache *ret;
+       D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
+-      spin_lock (&c->inocache_lock);
++
+       ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
+       while (ret && ret->ino < ino) {
+               ret = ret->next;
+       }
+-      spin_unlock(&c->inocache_lock);
+-
+       if (ret && ret->ino != ino)
+               ret = NULL;
+@@ -290,6 +490,8 @@
+ {
+       struct jffs2_inode_cache **prev;
+       D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
++      BUG_ON(!new->ino);
++
+       spin_lock(&c->inocache_lock);
+       
+       prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
+@@ -299,13 +501,14 @@
+       }
+       new->next = *prev;
+       *prev = new;
++
+       spin_unlock(&c->inocache_lock);
+ }
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
+ {
+       struct jffs2_inode_cache **prev;
+-      D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
++      D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+       spin_lock(&c->inocache_lock);
+       
+       prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
+@@ -316,6 +519,15 @@
+       if ((*prev) == old) {
+               *prev = old->next;
+       }
++
++      /* Free it now unless it's in READING or CLEARING state, which
++         are the transitions upon read_inode() and clear_inode(). The
++         rest of the time we know nobody else is looking at it, and 
++         if it's held by read_inode() or clear_inode() they'll free it
++         for themselves. */
++      if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
++              jffs2_free_inode_cache(old);
++
+       spin_unlock(&c->inocache_lock);
+ }
+@@ -328,7 +540,6 @@
+               this = c->inocache_list[i];
+               while (this) {
+                       next = this->next;
+-                      D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
+                       jffs2_free_inode_cache(this);
+                       this = next;
+               }
+@@ -352,3 +563,128 @@
+       }
+ }
+       
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
++{
++      /* The common case in lookup is that there will be a node 
++         which precisely matches. So we go looking for that first */
++      struct rb_node *next;
++      struct jffs2_node_frag *prev = NULL;
++      struct jffs2_node_frag *frag = NULL;
++
++      D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
++
++      next = fragtree->rb_node;
++
++      while(next) {
++              frag = rb_entry(next, struct jffs2_node_frag, rb);
++
++              D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
++                        frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
++              if (frag->ofs + frag->size <= offset) {
++                      D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
++                                frag->ofs, frag->ofs+frag->size));
++                      /* Remember the closest smaller match on the way down */
++                      if (!prev || frag->ofs > prev->ofs)
++                              prev = frag;
++                      next = frag->rb.rb_right;
++              } else if (frag->ofs > offset) {
++                      D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
++                                frag->ofs, frag->ofs+frag->size));
++                      next = frag->rb.rb_left;
++              } else {
++                      D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
++                                frag->ofs, frag->ofs+frag->size));
++                      return frag;
++              }
++      }
++
++      /* Exact match not found. Go back up looking at each parent,
++         and return the closest smaller one */
++
++      if (prev)
++              D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
++                        prev->ofs, prev->ofs+prev->size));
++      else 
++              D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
++      
++      return prev;
++}
++
++/* Pass 'c' argument to indicate that nodes should be marked obsolete as
++   they're killed. */
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
++{
++      struct jffs2_node_frag *frag;
++      struct jffs2_node_frag *parent;
++
++      if (!root->rb_node)
++              return;
++
++      frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
++
++      while(frag) {
++              if (frag->rb.rb_left) {
++                      D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", 
++                                frag, frag->ofs, frag->ofs+frag->size));
++                      frag = frag_left(frag);
++                      continue;
++              }
++              if (frag->rb.rb_right) {
++                      D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", 
++                                frag, frag->ofs, frag->ofs+frag->size));
++                      frag = frag_right(frag);
++                      continue;
++              }
++
++              D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
++                        frag->ofs, frag->ofs+frag->size, frag->node,
++                        frag->node?frag->node->frags:0));
++                      
++              if (frag->node && !(--frag->node->frags)) {
++                      /* Not a hole, and it's the final remaining frag 
++                         of this node. Free the node */
++                      if (c)
++                              jffs2_mark_node_obsolete(c, frag->node->raw);
++                      
++                      jffs2_free_full_dnode(frag->node);
++              }
++              parent = frag_parent(frag);
++              if (parent) {
++                      if (frag_left(parent) == frag)
++                              parent->rb.rb_left = NULL;
++                      else 
++                              parent->rb.rb_right = NULL;
++              }
++
++              jffs2_free_node_frag(frag);
++              frag = parent;
++
++              cond_resched();
++      }
++}
++
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
++{
++      struct rb_node *parent = &base->rb;
++      struct rb_node **link = &parent;
++
++      D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
++                newfrag->ofs, newfrag->ofs+newfrag->size, base));
++
++      while (*link) {
++              parent = *link;
++              base = rb_entry(parent, struct jffs2_node_frag, rb);
++      
++              D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
++              if (newfrag->ofs > base->ofs)
++                      link = &base->rb.rb_right;
++              else if (newfrag->ofs < base->ofs)
++                      link = &base->rb.rb_left;
++              else {
++                      printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
++                      BUG();
++              }
++      }
++
++      rb_link_node(&newfrag->rb, &base->rb, link);
++}
+--- linux-2.4.21/fs/jffs2/nodelist.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.h
+@@ -1,48 +1,35 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
++#ifndef __JFFS2_NODELIST_H__
++#define __JFFS2_NODELIST_H__
++
+ #include <linux/config.h>
+ #include <linux/fs.h>
+-
++#include <linux/types.h>
++#include <linux/jffs2.h>
+ #include <linux/jffs2_fs_sb.h>
+ #include <linux/jffs2_fs_i.h>
++#ifdef __ECOS
++#include "os-ecos.h"
++#else
++#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
++#include "os-linux.h"
++#endif
++
+ #ifndef CONFIG_JFFS2_FS_DEBUG
+-#define CONFIG_JFFS2_FS_DEBUG 2
++#define CONFIG_JFFS2_FS_DEBUG 1
+ #endif
+ #if CONFIG_JFFS2_FS_DEBUG > 0
+@@ -57,6 +44,39 @@
+ #define D2(x)
+ #endif
++#define JFFS2_NATIVE_ENDIAN
++
++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
++   whatever OS we're actually running on here too. */
++
++#if defined(JFFS2_NATIVE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){x})
++#define cpu_to_je32(x) ((jint32_t){x})
++#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
++
++#define je16_to_cpu(x) ((x).v16)
++#define je32_to_cpu(x) ((x).v32)
++#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
++#elif defined(JFFS2_BIG_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (be16_to_cpu(x.v16))
++#define je32_to_cpu(x) (be32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
++#elif defined(JFFS2_LITTLE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (le16_to_cpu(x.v16))
++#define je32_to_cpu(x) (le32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
++#else 
++#error wibble
++#endif
++
+ /*
+   This is all we need to keep in-core for each raw node during normal
+   operation. As and when we do read_inode on a particular inode, we can
+@@ -71,27 +91,21 @@
+               for this inode instead. The inode_cache will have NULL in the first
+               word so you know when you've got there :) */
+       struct jffs2_raw_node_ref *next_phys;
+-      //      __u32 ino;
+-      __u32 flash_offset;
+-      __u32 totlen;
+-//    __u16 nodetype;
++      uint32_t flash_offset;
++      uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
++};
+       
+         /* flash_offset & 3 always has to be zero, because nodes are
+          always aligned at 4 bytes. So we have a couple of extra bits
+-         to play with. So we set the least significant bit to 1 to
+-         signify that the node is obsoleted by later nodes.
+-      */
+-};
+-
+-/* 
+-   Used for keeping track of deletion nodes &c, which can only be marked
+-   as obsolete when the node which they mark as deleted has actually been 
+-   removed from the flash.
+-*/
+-struct jffs2_raw_node_ref_list {
+-      struct jffs2_raw_node_ref *rew;
+-      struct jffs2_raw_node_ref_list *next;
+-};
++         to play with, which indicate the node's status; see below: */ 
++#define REF_UNCHECKED 0       /* We haven't yet checked the CRC or built its inode */
++#define REF_OBSOLETE  1       /* Obsolete, can be completely ignored */
++#define REF_PRISTINE  2       /* Completely clean. GC without looking */
++#define REF_NORMAL    3       /* Possibly overlapped. Read the page and write again on GC */
++#define ref_flags(ref)                ((ref)->flash_offset & 3)
++#define ref_offset(ref)               ((ref)->flash_offset & ~3)
++#define ref_obsolete(ref)     (((ref)->flash_offset & 3) == REF_OBSOLETE)
++#define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
+ /* For each inode in the filesystem, we need to keep a record of
+    nlink, because it would be a PITA to scan the whole directory tree
+@@ -101,20 +115,30 @@
+    a pointer to the first physical node which is part of this inode, too.
+ */
+ struct jffs2_inode_cache {
+-      struct jffs2_scan_info *scan; /* Used during scan to hold
+-              temporary lists of nodes, and later must be set to
++      struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
++              temporary lists of dirents, and later must be set to
+               NULL to mark the end of the raw_node_ref->next_in_ino
+               chain. */
+       struct jffs2_inode_cache *next;
+       struct jffs2_raw_node_ref *nodes;
+-      __u32 ino;
++      uint32_t ino;
+       int nlink;
++      int state;
+ };
+-struct jffs2_scan_info {
+-      struct jffs2_full_dirent *dents;
+-      struct jffs2_tmp_dnode_info *tmpnodes;
+-};
++/* Inode states for 'state' above. We need the 'GC' state to prevent
++   someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
++   node without going through all the iget() nonsense */
++#define INO_STATE_UNCHECKED   0       /* CRC checks not yet done */
++#define INO_STATE_CHECKING    1       /* CRC checks in progress */
++#define INO_STATE_PRESENT     2       /* In core */
++#define INO_STATE_CHECKEDABSENT       3       /* Checked, cleared again */
++#define INO_STATE_GC          4       /* GCing a 'pristine' node */
++#define INO_STATE_READING     5       /* In read_inode() */
++#define INO_STATE_CLEARING    6       /* In clear_inode() */
++
++#define INOCACHE_HASHSIZE 128
++
+ /*
+   Larger representation of a raw node, kept in-core only when the 
+   struct inode for this particular ino is instantiated.
+@@ -123,12 +147,11 @@
+ struct jffs2_full_dnode
+ {
+       struct jffs2_raw_node_ref *raw;
+-      __u32 ofs; /* Don't really need this, but optimisation */
+-      __u32 size;
+-      __u32 frags; /* Number of fragments which currently refer
++      uint32_t ofs; /* The offset to which the data of this node belongs */
++      uint32_t size;
++      uint32_t frags; /* Number of fragments which currently refer
+                       to this node. When this reaches zero, 
+-                      the node is obsolete.
+-                   */
++                      the node is obsolete.  */
+ };
+ /* 
+@@ -140,144 +163,265 @@
+ {
+       struct jffs2_tmp_dnode_info *next;
+       struct jffs2_full_dnode *fn;
+-      __u32 version;
++      uint32_t version;
+ };       
+ struct jffs2_full_dirent
+ {
+       struct jffs2_raw_node_ref *raw;
+       struct jffs2_full_dirent *next;
+-      __u32 version;
+-      __u32 ino; /* == zero for unlink */
++      uint32_t version;
++      uint32_t ino; /* == zero for unlink */
+       unsigned int nhash;
+       unsigned char type;
+       unsigned char name[0];
+ };
++
+ /*
+   Fragments - used to build a map of which raw node to obtain 
+   data from for each part of the ino
+ */
+ struct jffs2_node_frag
+ {
+-      struct jffs2_node_frag *next;
++      struct rb_node rb;
+       struct jffs2_full_dnode *node; /* NULL for holes */
+-      __u32 size;
+-      __u32 ofs; /* Don't really need this, but optimisation */
++      uint32_t size;
++      uint32_t ofs; /* The offset to which this fragment belongs */
+ };
+ struct jffs2_eraseblock
+ {
+       struct list_head list;
+       int bad_count;
+-      __u32 offset;           /* of this block in the MTD */
++      uint32_t offset;                /* of this block in the MTD */
+-      __u32 used_size;
+-      __u32 dirty_size;
+-      __u32 free_size;        /* Note that sector_size - free_size
++      uint32_t unchecked_size;
++      uint32_t used_size;
++      uint32_t dirty_size;
++      uint32_t wasted_size;
++      uint32_t free_size;     /* Note that sector_size - free_size
+                                  is the address of the first free space */
+       struct jffs2_raw_node_ref *first_node;
+       struct jffs2_raw_node_ref *last_node;
+       struct jffs2_raw_node_ref *gc_node;     /* Next node to be garbage collected */
+-
+-      /* For deletia. When a dirent node in this eraseblock is
+-         deleted by a node elsewhere, that other node can only 
+-         be marked as obsolete when this block is actually erased.
+-         So we keep a list of the nodes to mark as obsolete when
+-         the erase is completed.
+-      */
+-      // MAYBE        struct jffs2_raw_node_ref_list *deletia;
+ };
+ #define ACCT_SANITY_CHECK(c, jeb) do { \
+-      if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
+-              printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
+-              printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
+-              jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
++              struct jffs2_eraseblock *___j = jeb; \
++              if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
++              printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
++              printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
++              ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
+               BUG(); \
+       } \
+-      if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
++      if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
+               printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
+-              printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
+-              c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
++              printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
++              c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
+               BUG(); \
+       } \
+ } while(0)
++static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
++{
++      struct jffs2_raw_node_ref *ref;
++      int i=0;
++
++      printk(KERN_NOTICE);
++      for (ref = jeb->first_node; ref; ref = ref->next_phys) {
++              printk("%08x->", ref_offset(ref));
++              if (++i == 8) {
++                      i = 0;
++                      printk("\n" KERN_NOTICE);
++              }
++      }
++      printk("\n");
++}
++
++
+ #define ACCT_PARANOIA_CHECK(jeb) do { \
+-              __u32 my_used_size = 0; \
++              uint32_t my_used_size = 0; \
++              uint32_t my_unchecked_size = 0; \
+               struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
+               while (ref2) { \
+-                      if (!(ref2->flash_offset & 1)) \
+-                              my_used_size += ref2->totlen; \
++                      if (unlikely(ref2->flash_offset < jeb->offset || \
++                                   ref2->flash_offset > jeb->offset + c->sector_size)) { \
++                              printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
++                                     ref_offset(ref2), jeb->offset); \
++                              paranoia_failed_dump(jeb); \
++                              BUG(); \
++                      } \
++                      if (ref_flags(ref2) == REF_UNCHECKED) \
++                              my_unchecked_size += ref_totlen(c, jeb, ref2); \
++                      else if (!ref_obsolete(ref2)) \
++                              my_used_size += ref_totlen(c, jeb, ref2); \
++                      if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
++                                if (!ref2->next_phys) \
++                                     printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
++                                           ref2, ref_offset(ref2), ref2->next_phys, \
++                                           jeb->last_node, ref_offset(jeb->last_node)); \
++                                else \
++                                       printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
++                                           ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
++                                           jeb->last_node, ref_offset(jeb->last_node)); \
++                              paranoia_failed_dump(jeb); \
++                              BUG(); \
++                      } \
+                       ref2 = ref2->next_phys; \
+               } \
+               if (my_used_size != jeb->used_size) { \
+                       printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
+                       BUG(); \
+               } \
++              if (my_unchecked_size != jeb->unchecked_size) { \
++                      printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
++                      BUG(); \
++              } \
+       } while(0)
++/* Calculate totlen from surrounding nodes or eraseblock */
++static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
++                                  struct jffs2_eraseblock *jeb,
++                                  struct jffs2_raw_node_ref *ref)
++{
++      uint32_t ref_end;
++      
++      if (ref->next_phys)
++              ref_end = ref_offset(ref->next_phys);
++      else {
++              if (!jeb)
++                      jeb = &c->blocks[ref->flash_offset / c->sector_size];
++
++              /* Last node in block. Use free_space */
++              BUG_ON(ref != jeb->last_node);
++              ref_end = jeb->offset + c->sector_size - jeb->free_size;
++      }
++      return ref_end - ref_offset(ref);
++}
++
++static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
++                                struct jffs2_eraseblock *jeb,
++                                struct jffs2_raw_node_ref *ref)
++{
++      uint32_t ret;
++
++      D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
++              printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
++                     jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
++              BUG();
++      })
++
++#if 1
++      ret = ref->__totlen;
++#else
++      /* This doesn't actually work yet */
++      ret = __ref_totlen(c, jeb, ref);
++      if (ret != ref->__totlen) {
++              printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
++                     ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
++                     ret, ref->__totlen);
++              if (!jeb)
++                      jeb = &c->blocks[ref->flash_offset / c->sector_size];
++              paranoia_failed_dump(jeb);
++              BUG();
++      }
++#endif
++      return ret;
++}
++
++
+ #define ALLOC_NORMAL  0       /* Normal allocation */
+ #define ALLOC_DELETION        1       /* Deletion node. Best to allow it */
+ #define ALLOC_GC      2       /* Space requested for GC. Give it or die */
++#define ALLOC_NORETRY 3       /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
+-#define JFFS2_RESERVED_BLOCKS_BASE 3                                          /* Number of free blocks there must be before we... */
+-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2)          /* ... allow a normal filesystem write */
+-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1)               /* ... allow a normal filesystem deletion */
+-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3)      /* ... wake up the GC thread */
+-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1)          /* ... pick a block from the bad_list to GC */
+-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE)            /* ... merge pages when garbage collecting */
++/* How much dirty space before it goes on the very_dirty_list */
++#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
++/* check if dirty space is more than 255 Byte */
++#define ISDIRTY(size) ((size) >  sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) 
+ #define PAD(x) (((x)+3)&~3)
+-static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
++static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+ {
+       while(raw->next_in_ino) {
+               raw = raw->next_in_ino;
+       }
+-      return ((struct jffs2_inode_cache *)raw)->ino;
++      return ((struct jffs2_inode_cache *)raw);
+ }
++static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
++{
++      struct rb_node *node = root->rb_node;
++
++      if (!node)
++              return NULL;
++      while(node->rb_left)
++              node = node->rb_left;
++      return rb_entry(node, struct jffs2_node_frag, rb);
++}
++#define rb_parent(rb) ((rb)->rb_parent)
++#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
++#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
++#define frag_erase(frag, list) rb_erase(&frag->rb, list);
++
+ /* nodelist.c */
+-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
++D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
+-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                         struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+-                        __u32 *highest_version, __u32 *latest_mctime,
+-                        __u32 *mctime_ver);
++                        uint32_t *highest_version, uint32_t *latest_mctime,
++                        uint32_t *mctime_ver);
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
+ void jffs2_free_ino_caches(struct jffs2_sb_info *c);
+ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
++struct rb_node *rb_next(struct rb_node *);
++struct rb_node *rb_prev(struct rb_node *);
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
+ /* nodemgmt.c */
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
++int jffs2_thread_should_wake(struct jffs2_sb_info *c);
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
+ void jffs2_complete_reservation(struct jffs2_sb_info *c);
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
++void jffs2_dump_block_lists(struct jffs2_sb_info *c);
+ /* write.c */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs,  __u32 *writelen);
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs,  __u32 *writelen);
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
++
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                          struct jffs2_raw_inode *ri, unsigned char *buf, 
++                          uint32_t offset, uint32_t writelen, uint32_t *retlen);
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
++
+ /* readinode.c */
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
+ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
+-void jffs2_read_inode (struct inode *);
+-void jffs2_clear_inode (struct inode *);
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
++                      uint32_t ino, struct jffs2_raw_inode *latest_node);
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+ /* malloc.c */
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
+-
+ int jffs2_create_slab_caches(void);
+ void jffs2_destroy_slab_caches(void);
+@@ -301,54 +445,31 @@
+ /* gc.c */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+-/* background.c */
+-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+-
+-/* dir.c */
+-extern struct file_operations jffs2_dir_operations;
+-extern struct inode_operations jffs2_dir_inode_operations;
+-
+-/* file.c */
+-extern struct file_operations jffs2_file_operations;
+-extern struct inode_operations jffs2_file_inode_operations;
+-extern struct address_space_operations jffs2_file_address_operations;
+-int jffs2_null_fsync(struct file *, struct dentry *, int);
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
+-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
+-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
+-int jffs2_readpage (struct file *, struct page *);
+-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
+-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
+-
+-/* ioctl.c */
+-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+-
+ /* read.c */
+-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+-
+-/* compr.c */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                           __u32 *datalen, __u32 *cdatalen);
+-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
+-                   unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                   struct jffs2_full_dnode *fd, unsigned char *buf,
++                   int ofs, int len);
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                         unsigned char *buf, uint32_t offset, uint32_t len);
++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+ /* scan.c */
+ int jffs2_scan_medium(struct jffs2_sb_info *c);
++void jffs2_rotate_lists(struct jffs2_sb_info *c);
+ /* build.c */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c);
+-
+-/* symlink.c */
+-extern struct inode_operations jffs2_symlink_inode_operations;
++int jffs2_do_mount_fs(struct jffs2_sb_info *c);
+ /* erase.c */
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
+-/* compr_zlib.c */
+-int jffs2_zlib_init(void);
+-void jffs2_zlib_exit(void);
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++/* wbuf.c */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++#endif
++
++#endif /* __JFFS2_NODELIST_H__ */
+--- linux-2.4.21/fs/jffs2/nodemgmt.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodemgmt.c
+@@ -1,45 +1,21 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/sched.h> /* For cond_resched() */
+ #include "nodelist.h"
+ /**
+@@ -62,53 +38,95 @@
+  *    for the requested allocation.
+  */
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  __u32 minsize, __u32 *ofs, __u32 *len);
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize, uint32_t *ofs, uint32_t *len);
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
+ {
+       int ret = -EAGAIN;
+-      int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
++      int blocksneeded = c->resv_blocks_write;
+       /* align it */
+       minsize = PAD(minsize);
+-      if (prio == ALLOC_DELETION)
+-              blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
+-
+       D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
+       down(&c->alloc_sem);
+       D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+-      spin_lock_bh(&c->erase_completion_lock);
++      spin_lock(&c->erase_completion_lock);
+-      /* this needs a little more thought */
++      /* this needs a little more thought (true <tglx> :)) */
+       while(ret == -EAGAIN) {
+               while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+                       int ret;
++                      uint32_t dirty, avail;
++
++                      /* calculate real dirty size
++                       * dirty_size contains blocks on erase_pending_list
++                       * those blocks are counted in c->nr_erasing_blocks.
++                       * If one block is actually erased, it is not longer counted as dirty_space
++                       * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++                       * with c->nr_erasing_blocks * c->sector_size again.
++                       * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++                       * This helps us to force gc and pick eventually a clean block to spread the load.
++                       * We add unchecked_size here, as we hopefully will find some space to use.
++                       * This will affect the sum only once, as gc first finishes checking
++                       * of nodes.
++                       */
++                      dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
++                      if (dirty < c->nospc_dirty_size) {
++                              if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++                                      printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
++                                      break;
++                              }
++                              D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
++                                        dirty, c->unchecked_size, c->sector_size));
++
++                              spin_unlock(&c->erase_completion_lock);
++                              up(&c->alloc_sem);
++                              return -ENOSPC;
++                      }
++                      
++                      /* Calc possibly available space. Possibly available means that we
++                       * don't know, if unchecked size contains obsoleted nodes, which could give us some
++                       * more usable space. This will affect the sum only once, as gc first finishes checking
++                       * of nodes.
++                       + Return -ENOSPC, if the maximum possibly available space is less or equal than 
++                       * blocksneeded * sector_size.
++                       * This blocks endless gc looping on a filesystem, which is nearly full, even if
++                       * the check above passes.
++                       */
++                      avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
++                      if ( (avail / c->sector_size) <= blocksneeded) {
++                              if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++                                      printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
++                                      break;
++                              }
++                              D1(printk(KERN_DEBUG "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
++                                        avail, blocksneeded * c->sector_size));
++                              spin_unlock(&c->erase_completion_lock);
+                       up(&c->alloc_sem);
+-                      if (c->dirty_size < c->sector_size) {
+-                              D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
+-                              spin_unlock_bh(&c->erase_completion_lock);
+                               return -ENOSPC;
+                       }
+-                      D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+-                                c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
+-                                c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+-                      spin_unlock_bh(&c->erase_completion_lock);
++
++                      up(&c->alloc_sem);
++
++                      D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
++                                c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
++                                c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
++                      spin_unlock(&c->erase_completion_lock);
+                       
+                       ret = jffs2_garbage_collect_pass(c);
+                       if (ret)
+                               return ret;
+-                      if (current->need_resched)
+-                              schedule();
++                      cond_resched();
+                       if (signal_pending(current))
+                               return -EINTR;
+                       down(&c->alloc_sem);
+-                      spin_lock_bh(&c->erase_completion_lock);
++                      spin_lock(&c->erase_completion_lock);
+               }
+               ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+@@ -116,45 +134,72 @@
+                       D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
+               }
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
++      spin_unlock(&c->erase_completion_lock);
+       if (ret)
+               up(&c->alloc_sem);
+       return ret;
+ }
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+       int ret = -EAGAIN;
+       minsize = PAD(minsize);
+       D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
+-      spin_lock_bh(&c->erase_completion_lock);
++      spin_lock(&c->erase_completion_lock);
+       while(ret == -EAGAIN) {
+               ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+               if (ret) {
+                       D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+               }
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
++      spin_unlock(&c->erase_completion_lock);
+       return ret;
+ }
+ /* Called with alloc sem _and_ erase_completion_lock */
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  __u32 minsize, __u32 *ofs, __u32 *len)
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+       struct jffs2_eraseblock *jeb = c->nextblock;
+       
+  restart:
+       if (jeb && minsize > jeb->free_size) {
+               /* Skip the end of this block and file it as having some dirty space */
+-              c->dirty_size += jeb->free_size;
++              /* If there's a pending write to it, flush now */
++              if (jffs2_wbuf_dirty(c)) {
++                      spin_unlock(&c->erase_completion_lock);
++                      D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));                           
++                      jffs2_flush_wbuf_pad(c);
++                      spin_lock(&c->erase_completion_lock);
++                      jeb = c->nextblock;
++                      goto restart;
++              }
++              c->wasted_size += jeb->free_size;
+               c->free_size -= jeb->free_size;
+-              jeb->dirty_size += jeb->free_size;
++              jeb->wasted_size += jeb->free_size;
+               jeb->free_size = 0;
++              
++              /* Check, if we have a dirty block now, or if it was dirty already */
++              if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
++                      c->dirty_size += jeb->wasted_size;
++                      c->wasted_size -= jeb->wasted_size;
++                      jeb->dirty_size += jeb->wasted_size;
++                      jeb->wasted_size = 0;
++                      if (VERYDIRTY(c, jeb->dirty_size)) {
++                              D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++                                jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++                              list_add_tail(&jeb->list, &c->very_dirty_list);
++                      } else {
+               D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+               list_add_tail(&jeb->list, &c->dirty_list);
++                      }
++              } else { 
++                      D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++                        jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++                      list_add_tail(&jeb->list, &c->clean_list);
++              }
+               c->nextblock = jeb = NULL;
+       }
+       
+@@ -164,33 +209,44 @@
+               if (list_empty(&c->free_list)) {
+-                      DECLARE_WAITQUEUE(wait, current);
++                      if (!c->nr_erasing_blocks && 
++                          !list_empty(&c->erasable_list)) {
++                              struct jffs2_eraseblock *ejeb;
++
++                              ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
++                              list_del(&ejeb->list);
++                              list_add_tail(&ejeb->list, &c->erase_pending_list);
++                              c->nr_erasing_blocks++;
++                              jffs2_erase_pending_trigger(c);
++                              D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
++                                        ejeb->offset));
++                      }
++
++                      if (!c->nr_erasing_blocks && 
++                          !list_empty(&c->erasable_pending_wbuf_list)) {
++                              D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
++                              /* c->nextblock is NULL, no update to c->nextblock allowed */                       
++                              spin_unlock(&c->erase_completion_lock);
++                              jffs2_flush_wbuf_pad(c);
++                              spin_lock(&c->erase_completion_lock);
++                              /* Have another go. It'll be on the erasable_list now */
++                              return -EAGAIN;
++                      }
+                       
+                       if (!c->nr_erasing_blocks) {
+-//                    if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
+                               /* Ouch. We're in GC, or we wouldn't have got here.
+                                  And there's no space left. At all. */
+-                              printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
+-                                     c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
++                              printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", 
++                                     c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", 
++                                     list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+                               return -ENOSPC;
+                       }
+-                      /* Make sure this can't deadlock. Someone has to start the erases
+-                         of erase_pending blocks */
+-                      set_current_state(TASK_INTERRUPTIBLE);
+-                      add_wait_queue(&c->erase_wait, &wait);
+-                      D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
+-                                c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+-                      if (!list_empty(&c->erase_pending_list)) {
+-                              D1(printk(KERN_DEBUG "Triggering pending erases\n"));
+-                              jffs2_erase_pending_trigger(c);
+-                      }
+-                      spin_unlock_bh(&c->erase_completion_lock);
+-                      schedule();
+-                      remove_wait_queue(&c->erase_wait, &wait);
+-                      spin_lock_bh(&c->erase_completion_lock);
+-                      if (signal_pending(current)) {
+-                              return -EINTR;
+-                      }
++
++                      spin_unlock(&c->erase_completion_lock);
++                      /* Don't wait for it; just erase one right now */
++                      jffs2_erase_pending_blocks(c, 1);
++                      spin_lock(&c->erase_completion_lock);
++
+                       /* An erase may have failed, decreasing the
+                          amount of free space available. So we must
+                          restart from the beginning */
+@@ -201,7 +257,8 @@
+               list_del(next);
+               c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
+               c->nr_free_blocks--;
+-              if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
++
++              if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+                       printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
+                       goto restart;
+               }
+@@ -210,6 +267,20 @@
+          enough space */
+       *ofs = jeb->offset + (c->sector_size - jeb->free_size);
+       *len = jeb->free_size;
++
++      if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
++          !jeb->first_node->next_in_ino) {
++              /* Only node in it beforehand was a CLEANMARKER node (we think). 
++                 So mark it obsolete now that there's going to be another node
++                 in the block. This will reduce used_size to zero but We've 
++                 already set c->nextblock so that jffs2_mark_node_obsolete()
++                 won't try to refile it to the dirty_list.
++              */
++              spin_unlock(&c->erase_completion_lock);
++              jffs2_mark_node_obsolete(c, jeb->first_node);
++              spin_lock(&c->erase_completion_lock);
++      }
++
+       D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
+       return 0;
+ }
+@@ -217,9 +288,9 @@
+ /**
+  *    jffs2_add_physical_node_ref - add a physical node reference to the list
+  *    @c: superblock info
+- *    @ofs: physical location of this physical node
++ *    @new: new node reference to add
+  *    @len: length of this physical node
+- *    @ino: inode number with which this physical node is associated
++ *    @dirty: dirty flag for new node
+  *
+  *    Should only be used to report nodes for which space has been allocated 
+  *    by jffs2_reserve_space.
+@@ -227,47 +298,61 @@
+  *    Must be called with the alloc_sem held.
+  */
+  
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
+ {
+       struct jffs2_eraseblock *jeb;
++      uint32_t len;
+-      len = PAD(len);
+-      jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
+-      D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
++      jeb = &c->blocks[new->flash_offset / c->sector_size];
++      len = ref_totlen(c, jeb, new);
++
++      D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
+ #if 1
+-      if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
++      /* we could get some obsolete nodes after nextblock was refiled
++         in wbuf.c */
++      if ((c->nextblock || !ref_obsolete(new))
++          &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
+               printk(KERN_WARNING "argh. node added in wrong place\n");
+               jffs2_free_raw_node_ref(new);
+               return -EINVAL;
+       }
+ #endif
++      spin_lock(&c->erase_completion_lock);
++
+       if (!jeb->first_node)
+               jeb->first_node = new;
+       if (jeb->last_node)
+               jeb->last_node->next_phys = new;
+       jeb->last_node = new;
+-      spin_lock_bh(&c->erase_completion_lock);
+       jeb->free_size -= len;
+       c->free_size -= len;
+-      if (dirty) {
+-              new->flash_offset |= 1;
++      if (ref_obsolete(new)) {
+               jeb->dirty_size += len;
+               c->dirty_size += len;
+       } else {
+               jeb->used_size += len;
+               c->used_size += len;
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
+-      if (!jeb->free_size && !jeb->dirty_size) {
++
++      if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
+               /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
+               D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++              if (jffs2_wbuf_dirty(c)) {
++                      /* Flush the last write in the block if it's outstanding */
++                      spin_unlock(&c->erase_completion_lock);
++                      jffs2_flush_wbuf_pad(c);
++                      spin_lock(&c->erase_completion_lock);
++              }
++
+               list_add_tail(&jeb->list, &c->clean_list);
+               c->nextblock = NULL;
+       }
+       ACCT_SANITY_CHECK(c,jeb);
+-      ACCT_PARANOIA_CHECK(jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
++
++      spin_unlock(&c->erase_completion_lock);
+       return 0;
+ }
+@@ -280,20 +365,34 @@
+       up(&c->alloc_sem);
+ }
++static inline int on_list(struct list_head *obj, struct list_head *head)
++{
++      struct list_head *this;
++
++      list_for_each(this, head) {
++              if (this == obj) {
++                      D1(printk("%p is on list at %p\n", obj, head));
++                      return 1;
++
++              }
++      }
++      return 0;
++}
++
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
+ {
+       struct jffs2_eraseblock *jeb;
+       int blocknr;
+       struct jffs2_unknown_node n;
+-      int ret;
+-      ssize_t retlen;
++      int ret, addedsize;
++      size_t retlen;
+       if(!ref) {
+               printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+               return;
+       }
+-      if (ref->flash_offset & 1) {
+-              D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
++      if (ref_obsolete(ref)) {
++              D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
+               return;
+       }
+       blocknr = ref->flash_offset / c->sector_size;
+@@ -302,91 +401,439 @@
+               BUG();
+       }
+       jeb = &c->blocks[blocknr];
+-      if (jeb->used_size < ref->totlen) {
++
++      if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
++          !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
++              /* Hm. This may confuse static lock analysis. If any of the above 
++                 three conditions is false, we're going to return from this 
++                 function without actually obliterating any nodes or freeing
++                 any jffs2_raw_node_refs. So we don't need to stop erases from
++                 happening, or protect against people holding an obsolete
++                 jffs2_raw_node_ref without the erase_completion_lock. */
++              down(&c->erase_free_sem);
++      }
++
++      spin_lock(&c->erase_completion_lock);
++
++      if (ref_flags(ref) == REF_UNCHECKED) {
++              D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
++                      printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
++                             ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
++                      BUG();
++              })
++              D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++              jeb->unchecked_size -= ref_totlen(c, jeb, ref);
++              c->unchecked_size -= ref_totlen(c, jeb, ref);
++      } else {
++              D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+               printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+-                     ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
++                             ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+               BUG();
++              })
++              D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++              jeb->used_size -= ref_totlen(c, jeb, ref);
++              c->used_size -= ref_totlen(c, jeb, ref);
+       }
+-      spin_lock_bh(&c->erase_completion_lock);
+-      jeb->used_size -= ref->totlen;
+-      jeb->dirty_size += ref->totlen;
+-      c->used_size -= ref->totlen;
+-      c->dirty_size += ref->totlen;
+-      ref->flash_offset |= 1;
++      // Take care, that wasted size is taken into concern
++      if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
++              D1(printk("Dirtying\n"));
++              addedsize = ref_totlen(c, jeb, ref);
++              jeb->dirty_size += ref_totlen(c, jeb, ref);
++              c->dirty_size += ref_totlen(c, jeb, ref);
++
++              /* Convert wasted space to dirty, if not a bad block */
++              if (jeb->wasted_size) {
++                      if (on_list(&jeb->list, &c->bad_used_list)) {
++                              D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
++                                        jeb->offset));
++                              addedsize = 0; /* To fool the refiling code later */
++                      } else {
++                              D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
++                                        jeb->wasted_size, jeb->offset));
++                              addedsize += jeb->wasted_size;
++                              jeb->dirty_size += jeb->wasted_size;
++                              c->dirty_size += jeb->wasted_size;
++                              c->wasted_size -= jeb->wasted_size;
++                              jeb->wasted_size = 0;
++                      }
++              }
++      } else {
++              D1(printk("Wasting\n"));
++              addedsize = 0;
++              jeb->wasted_size += ref_totlen(c, jeb, ref);
++              c->wasted_size += ref_totlen(c, jeb, ref);      
++      }
++      ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
+       
+       ACCT_SANITY_CHECK(c, jeb);
+-      ACCT_PARANOIA_CHECK(jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
+-      if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+-              /* Mount in progress. Don't muck about with the block
++      if (c->flags & JFFS2_SB_FLAG_SCANNING) {
++              /* Flash scanning is in progress. Don't muck about with the block
+                  lists because they're not ready yet, and don't actually
+                  obliterate nodes that look obsolete. If they weren't 
+                  marked obsolete on the flash at the time they _became_
+                  obsolete, there was probably a reason for that. */
+-              spin_unlock_bh(&c->erase_completion_lock);
++              spin_unlock(&c->erase_completion_lock);
++              /* We didn't lock the erase_free_sem */
+               return;
+       }
++
+       if (jeb == c->nextblock) {
+               D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+-      } else if (jeb == c->gcblock) {
+-              D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
+-       data and a few blocks free, and you just create new files and keep deleting/overwriting
+-       them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
+-       So we leave completely obsoleted blocks on the dirty_list and let the GC delete them 
+-       when it finds them there. That way, we still get the 'once in a while, take a clean block'
+-       to spread out the flash usage */
+-      } else if (!jeb->used_size) {
++      } else if (!jeb->used_size && !jeb->unchecked_size) {
++              if (jeb == c->gcblock) {
++                      D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
++                      c->gcblock = NULL;
++              } else {
+               D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+               list_del(&jeb->list);
++              }
++              if (jffs2_wbuf_dirty(c)) {
++                      D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
++                      list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
++              } else {
++                      if (jiffies & 127) {
++                              /* Most of the time, we just erase it immediately. Otherwise we
++                                 spend ages scanning it on mount, etc. */
+               D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+               list_add_tail(&jeb->list, &c->erase_pending_list);
+               c->nr_erasing_blocks++;
+               jffs2_erase_pending_trigger(c);
+-              //              OFNI_BS_2SFFJ(c)->s_dirt = 1;
++                      } else {
++                              /* Sometimes, however, we leave it elsewhere so it doesn't get
++                                 immediately reused, and we spread the load a bit. */
++                              D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++                              list_add_tail(&jeb->list, &c->erasable_list);
++                      }                               
++              }
+               D1(printk(KERN_DEBUG "Done OK\n"));
+-#endif
+-      } else if (jeb->dirty_size == ref->totlen) {
++      } else if (jeb == c->gcblock) {
++              D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
++      } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
+               D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
+               list_del(&jeb->list);
+               D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
+               list_add_tail(&jeb->list, &c->dirty_list);
++      } else if (VERYDIRTY(c, jeb->dirty_size) &&
++                 !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
++              D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
++              list_del(&jeb->list);
++              D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
++              list_add_tail(&jeb->list, &c->very_dirty_list);
++      } else {
++              D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
++                        jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); 
+       }
+-      spin_unlock_bh(&c->erase_completion_lock);
+-      if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
+-              return;
+-      if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++      spin_unlock(&c->erase_completion_lock);
++
++      if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
++              (c->flags & JFFS2_SB_FLAG_BUILDING)) {
++              /* We didn't lock the erase_free_sem */
+               return;
++      }
+-      D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
+-      ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
++      /* The erase_free_sem is locked, and has been since before we marked the node obsolete
++         and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
++         the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
++         by jffs2_free_all_node_refs() in erase.c. Which is nice. */
++
++      D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
++      ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+       if (ret) {
+-              printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
+-              return;
++              printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++              goto out_erase_sem;
+       }
+       if (retlen != sizeof(n)) {
+-              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+-              return;
++              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++              goto out_erase_sem;
+       }
+-      if (PAD(n.totlen) != PAD(ref->totlen)) {
+-              printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
+-              return;
++      if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
++              printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
++              goto out_erase_sem;
+       }
+-      if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
+-              D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
+-              return;
++      if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
++              D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
++              goto out_erase_sem;
+       }
+-      n.nodetype &= ~JFFS2_NODE_ACCURATE;
+-      ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
++      /* XXX FIXME: This is ugly now */
++      n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
++      ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+       if (ret) {
+-              printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
+-              return;
++              printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++              goto out_erase_sem;
+       }
+       if (retlen != sizeof(n)) {
+-              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+-              return;
++              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++              goto out_erase_sem;
++      }
++
++      /* Nodes which have been marked obsolete no longer need to be
++         associated with any inode. Remove them from the per-inode list.
++         
++         Note we can't do this for NAND at the moment because we need 
++         obsolete dirent nodes to stay on the lists, because of the
++         horridness in jffs2_garbage_collect_deletion_dirent(). Also
++         because we delete the inocache, and on NAND we need that to 
++         stay around until all the nodes are actually erased, in order
++         to stop us from giving the same inode number to another newly
++         created inode. */
++      if (ref->next_in_ino) {
++              struct jffs2_inode_cache *ic;
++              struct jffs2_raw_node_ref **p;
++
++              spin_lock(&c->erase_completion_lock);
++
++              ic = jffs2_raw_ref_to_ic(ref);
++              for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
++                      ;
++
++              *p = ref->next_in_ino;
++              ref->next_in_ino = NULL;
++
++              if (ic->nodes == (void *)ic)
++                      jffs2_del_ino_cache(c, ic);
++
++              spin_unlock(&c->erase_completion_lock);
+       }
++
++
++      /* Merge with the next node in the physical list, if there is one
++         and if it's also obsolete and if it doesn't belong to any inode */
++      if (ref->next_phys && ref_obsolete(ref->next_phys) &&
++          !ref->next_phys->next_in_ino) {
++              struct jffs2_raw_node_ref *n = ref->next_phys;
++              
++              spin_lock(&c->erase_completion_lock);
++
++              ref->__totlen += n->__totlen;
++              ref->next_phys = n->next_phys;
++                if (jeb->last_node == n) jeb->last_node = ref;
++              if (jeb->gc_node == n) {
++                      /* gc will be happy continuing gc on this node */
++                      jeb->gc_node=ref;
++              }
++              spin_unlock(&c->erase_completion_lock);
++
++              jffs2_free_raw_node_ref(n);
++      }
++      
++      /* Also merge with the previous node in the list, if there is one
++         and that one is obsolete */
++      if (ref != jeb->first_node ) {
++              struct jffs2_raw_node_ref *p = jeb->first_node;
++
++              spin_lock(&c->erase_completion_lock);
++
++              while (p->next_phys != ref)
++                      p = p->next_phys;
++              
++              if (ref_obsolete(p) && !ref->next_in_ino) {
++                      p->__totlen += ref->__totlen;
++                      if (jeb->last_node == ref) {
++                              jeb->last_node = p;
++                      }
++                      if (jeb->gc_node == ref) {
++                              /* gc will be happy continuing gc on this node */
++                              jeb->gc_node=p;
++                      }
++                      p->next_phys = ref->next_phys;
++                      jffs2_free_raw_node_ref(ref);
++              }
++              spin_unlock(&c->erase_completion_lock);
++      }
++ out_erase_sem:
++      up(&c->erase_free_sem);
++}
++
++#if CONFIG_JFFS2_FS_DEBUG >= 2
++void jffs2_dump_block_lists(struct jffs2_sb_info *c)
++{
++
++
++      printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
++      printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
++      printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
++      printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
++      printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
++      printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
++      printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
++      printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
++      printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
++      printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
++      printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
++
++      if (c->nextblock) {
++              printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                     c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
++      } else {
++              printk(KERN_DEBUG "nextblock: NULL\n");
++      }
++      if (c->gcblock) {
++              printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                     c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
++      } else {
++              printk(KERN_DEBUG "gcblock: NULL\n");
++      }
++      if (list_empty(&c->clean_list)) {
++              printk(KERN_DEBUG "clean_list: empty\n");
++      } else {
++              struct list_head *this;
++              int     numblocks = 0;
++              uint32_t dirty = 0;
++
++              list_for_each(this, &c->clean_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      numblocks ++;
++                      dirty += jeb->wasted_size;
++                      printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++              printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
++      }
++      if (list_empty(&c->very_dirty_list)) {
++              printk(KERN_DEBUG "very_dirty_list: empty\n");
++      } else {
++              struct list_head *this;
++              int     numblocks = 0;
++              uint32_t dirty = 0;
++
++              list_for_each(this, &c->very_dirty_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      numblocks ++;
++                      dirty += jeb->dirty_size;
++                      printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++              printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++                      numblocks, dirty, dirty / numblocks);
++      }
++      if (list_empty(&c->dirty_list)) {
++              printk(KERN_DEBUG "dirty_list: empty\n");
++      } else {
++              struct list_head *this;
++              int     numblocks = 0;
++              uint32_t dirty = 0;
++
++              list_for_each(this, &c->dirty_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      numblocks ++;
++                      dirty += jeb->dirty_size;
++                      printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++              printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++                      numblocks, dirty, dirty / numblocks);
++      }
++      if (list_empty(&c->erasable_list)) {
++              printk(KERN_DEBUG "erasable_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->erasable_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->erasing_list)) {
++              printk(KERN_DEBUG "erasing_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->erasing_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->erase_pending_list)) {
++              printk(KERN_DEBUG "erase_pending_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->erase_pending_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->erasable_pending_wbuf_list)) {
++              printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->erasable_pending_wbuf_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->free_list)) {
++              printk(KERN_DEBUG "free_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->free_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->bad_list)) {
++              printk(KERN_DEBUG "bad_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->bad_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++      if (list_empty(&c->bad_used_list)) {
++              printk(KERN_DEBUG "bad_used_list: empty\n");
++      } else {
++              struct list_head *this;
++
++              list_for_each(this, &c->bad_used_list) {
++                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++                      printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++                             jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++              }
++      }
++}
++#endif /* CONFIG_JFFS2_FS_DEBUG */
++
++int jffs2_thread_should_wake(struct jffs2_sb_info *c)
++{
++      int ret = 0;
++      uint32_t dirty;
++
++      if (c->unchecked_size) {
++              D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
++                        c->unchecked_size, c->checked_ino));
++              return 1;
++      }
++
++      /* dirty_size contains blocks on erase_pending_list
++       * those blocks are counted in c->nr_erasing_blocks.
++       * If one block is actually erased, it is not longer counted as dirty_space
++       * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++       * with c->nr_erasing_blocks * c->sector_size again.
++       * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++       * This helps us to force gc and pick eventually a clean block to spread the load.
++       */
++      dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
++
++      if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && 
++                      (dirty > c->nospc_dirty_size)) 
++              ret = 1;
++
++      D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", 
++                c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
++
++      return ret;
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/os-linux.h
+@@ -0,0 +1,227 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2002-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __JFFS2_OS_LINUX_H__
++#define __JFFS2_OS_LINUX_H__
++#include <linux/version.h>
++
++/* JFFS2 uses Linux mode bits natively -- no need for conversion */
++#define os_to_jffs2_mode(x) (x)
++#define jffs2_to_os_mode(x) (x)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define kstatfs statfs
++#endif
++
++struct kstatfs;
++struct kvec;
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
++#define OFNI_EDONI_2SFFJ(f)  (&(f)->vfs_inode)
++#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
++#define OFNI_BS_2SFFJ(c)  ((struct super_block *)c->os_priv)
++#elif defined(JFFS2_OUT_OF_KERNEL)
++#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
++#define OFNI_EDONI_2SFFJ(f)  ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
++#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#else
++#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++#define OFNI_EDONI_2SFFJ(f)  ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#endif
++
++
++#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
++#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
++#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
++#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
++#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
++#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
++#else
++#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#endif
++
++/* Urgh. The things we do to keep the 2.4 build working */
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
++#define ITIME(sec) ((struct timespec){sec, 0})
++#define I_SEC(tv) ((tv).tv_sec)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
++#else
++#define ITIME(x) (x)
++#define I_SEC(x) (x)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
++#endif
++
++#define sleep_on_spinunlock(wq, s)                            \
++      do {                                                    \
++              DECLARE_WAITQUEUE(__wait, current);             \
++              add_wait_queue((wq), &__wait);                  \
++              set_current_state(TASK_UNINTERRUPTIBLE);        \
++              spin_unlock(s);                                 \
++              schedule();                                     \
++              remove_wait_queue((wq), &__wait);               \
++      } while(0)
++
++static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
++{
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++      f->highest_version = 0;
++      f->fragtree = RB_ROOT;
++      f->metadata = NULL;
++      f->dents = NULL;
++      f->flags = 0;
++      f->usercompr = 0;
++#else
++      memset(f, 0, sizeof(*f));
++      init_MUTEX_LOCKED(&f->sem);
++#endif
++}
++
++
++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
++
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
++#define jffs2_can_mark_obsolete(c) (1)
++#define jffs2_cleanmarker_oob(c) (0)
++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
++
++#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
++#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
++#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
++#define jffs2_nand_flash_setup(c) (0)
++#define jffs2_nand_flash_cleanup(c) do {} while(0)
++#define jffs2_wbuf_dirty(c) (0)
++#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
++#define jffs2_wbuf_timeout NULL
++#define jffs2_wbuf_process NULL
++#define jffs2_nor_ecc(c) (0)
++#define jffs2_dataflash(c) (0)
++#define jffs2_nor_ecc_flash_setup(c) (0)
++#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
++
++#else /* NAND and/or ECC'd NOR support present */
++
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
++#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
++
++#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
++
++/* wbuf.c */
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
++int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
++void jffs2_wbuf_timeout(unsigned long data);
++void jffs2_wbuf_process(void *data);
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
++
++#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
++
++#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
++int jffs2_dataflash_setup(struct jffs2_sb_info *c);
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
++
++#endif /* WRITEBUFFER */
++
++/* erase.c */
++static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++{
++      OFNI_BS_2SFFJ(c)->s_dirt = 1;
++}
++
++/* background.c */
++int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
++
++/* dir.c */
++extern struct file_operations jffs2_dir_operations;
++extern struct inode_operations jffs2_dir_inode_operations;
++
++/* file.c */
++extern struct file_operations jffs2_file_operations;
++extern struct inode_operations jffs2_file_inode_operations;
++extern struct address_space_operations jffs2_file_address_operations;
++int jffs2_fsync(struct file *, struct dentry *, int);
++int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
++int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
++int jffs2_readpage (struct file *, struct page *);
++int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
++int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
++
++/* ioctl.c */
++int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++
++/* symlink.c */
++extern struct inode_operations jffs2_symlink_inode_operations;
++
++/* fs.c */
++int jffs2_setattr (struct dentry *, struct iattr *);
++void jffs2_read_inode (struct inode *);
++void jffs2_clear_inode (struct inode *);
++void jffs2_dirty_inode(struct inode *inode);
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
++                             struct jffs2_raw_inode *ri);
++int jffs2_statfs (struct super_block *, struct kstatfs *);
++void jffs2_write_super (struct super_block *);
++int jffs2_remount_fs (struct super_block *, int *, char *);
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++                          struct jffs2_inode_info *f);
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++                                            int inum, int nlink);
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, 
++                                 struct jffs2_inode_info *f, 
++                                 unsigned long offset,
++                                 unsigned long *priv);
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++                         unsigned char *pg,
++                         unsigned long *priv);
++int jffs2_flash_setup(struct jffs2_sb_info *c);
++void jffs2_flash_cleanup(struct jffs2_sb_info *c);
++     
++
++/* writev.c */
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, 
++                     unsigned long count, loff_t to, size_t *retlen);
++
++
++#endif /* __JFFS2_OS_LINUX_H__ */
++
++
+--- linux-2.4.21/fs/jffs2/pushpull.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/pushpull.h
+@@ -1,42 +1,21 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #ifndef __PUSHPULL_H__
+ #define __PUSHPULL_H__
++
++#include <linux/errno.h>
++
+ struct pushpull {
+       unsigned char *buf;
+       unsigned int buflen;
+@@ -44,9 +23,36 @@
+       unsigned int reserve;
+ };
+-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
+-int pushbit(struct pushpull *pp, int bit, int use_reserved);
+-int pushedbits(struct pushpull *pp);
++
++static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
++{
++      pp->buf = buf;
++      pp->buflen = buflen;
++      pp->ofs = ofs;
++      pp->reserve = reserve;
++}
++
++static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
++{
++      if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
++              return -ENOSPC;
++      }
++
++      if (bit) {
++              pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
++      }
++      else {
++              pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
++      }
++      pp->ofs++;
++
++      return 0;
++}
++
++static inline int pushedbits(struct pushpull *pp)
++{
++      return pp->ofs;
++}
+ static inline int pullbit(struct pushpull *pp)
+ {
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/rbtree.c
+@@ -0,0 +1,363 @@
++/*
++  Red Black Trees
++  (C) 1999  Andrea Arcangeli <andrea@suse.de>
++  (C) 2002  David Woodhouse <dwmw2@infradead.org>
++  
++  This program is free software; you can redistribute it and/or modify
++  it under the terms of the GNU General Public License as published by
++  the Free Software Foundation; either version 2 of the License, or
++  (at your option) any later version.
++
++  This program is distributed in the hope that it will be useful,
++  but WITHOUT ANY WARRANTY; without even the implied warranty of
++  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++  GNU General Public License for more details.
++
++  You should have received a copy of the GNU General Public License
++  along with this program; if not, write to the Free Software
++  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++
++  $Id$
++*/
++
++#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */
++#error "Licence problem. eCos has its own rbtree code."
++#endif
++
++#include <linux/version.h>
++#include <linux/rbtree.h>
++
++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \
++   (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE))
++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * right = node->rb_right;
++
++      if ((node->rb_right = right->rb_left))
++              right->rb_left->rb_parent = node;
++      right->rb_left = node;
++
++      if ((right->rb_parent = node->rb_parent))
++      {
++              if (node == node->rb_parent->rb_left)
++                      node->rb_parent->rb_left = right;
++              else
++                      node->rb_parent->rb_right = right;
++      }
++      else
++              root->rb_node = right;
++      node->rb_parent = right;
++}
++
++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * left = node->rb_left;
++
++      if ((node->rb_left = left->rb_right))
++              left->rb_right->rb_parent = node;
++      left->rb_right = node;
++
++      if ((left->rb_parent = node->rb_parent))
++      {
++              if (node == node->rb_parent->rb_right)
++                      node->rb_parent->rb_right = left;
++              else
++                      node->rb_parent->rb_left = left;
++      }
++      else
++              root->rb_node = left;
++      node->rb_parent = left;
++}
++
++void rb_insert_color(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * parent, * gparent;
++
++      while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
++      {
++              gparent = parent->rb_parent;
++
++              if (parent == gparent->rb_left)
++              {
++                      {
++                              register struct rb_node * uncle = gparent->rb_right;
++                              if (uncle && uncle->rb_color == RB_RED)
++                              {
++                                      uncle->rb_color = RB_BLACK;
++                                      parent->rb_color = RB_BLACK;
++                                      gparent->rb_color = RB_RED;
++                                      node = gparent;
++                                      continue;
++                              }
++                      }
++
++                      if (parent->rb_right == node)
++                      {
++                              register struct rb_node * tmp;
++                              __rb_rotate_left(parent, root);
++                              tmp = parent;
++                              parent = node;
++                              node = tmp;
++                      }
++
++                      parent->rb_color = RB_BLACK;
++                      gparent->rb_color = RB_RED;
++                      __rb_rotate_right(gparent, root);
++              } else {
++                      {
++                              register struct rb_node * uncle = gparent->rb_left;
++                              if (uncle && uncle->rb_color == RB_RED)
++                              {
++                                      uncle->rb_color = RB_BLACK;
++                                      parent->rb_color = RB_BLACK;
++                                      gparent->rb_color = RB_RED;
++                                      node = gparent;
++                                      continue;
++                              }
++                      }
++
++                      if (parent->rb_left == node)
++                      {
++                              register struct rb_node * tmp;
++                              __rb_rotate_right(parent, root);
++                              tmp = parent;
++                              parent = node;
++                              node = tmp;
++                      }
++
++                      parent->rb_color = RB_BLACK;
++                      gparent->rb_color = RB_RED;
++                      __rb_rotate_left(gparent, root);
++              }
++      }
++
++      root->rb_node->rb_color = RB_BLACK;
++}
++
++static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
++                           struct rb_root * root)
++{
++      struct rb_node * other;
++
++      while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
++      {
++              if (parent->rb_left == node)
++              {
++                      other = parent->rb_right;
++                      if (other->rb_color == RB_RED)
++                      {
++                              other->rb_color = RB_BLACK;
++                              parent->rb_color = RB_RED;
++                              __rb_rotate_left(parent, root);
++                              other = parent->rb_right;
++                      }
++                      if ((!other->rb_left ||
++                           other->rb_left->rb_color == RB_BLACK)
++                          && (!other->rb_right ||
++                              other->rb_right->rb_color == RB_BLACK))
++                      {
++                              other->rb_color = RB_RED;
++                              node = parent;
++                              parent = node->rb_parent;
++                      }
++                      else
++                      {
++                              if (!other->rb_right ||
++                                  other->rb_right->rb_color == RB_BLACK)
++                              {
++                                      register struct rb_node * o_left;
++                                      if ((o_left = other->rb_left))
++                                              o_left->rb_color = RB_BLACK;
++                                      other->rb_color = RB_RED;
++                                      __rb_rotate_right(other, root);
++                                      other = parent->rb_right;
++                              }
++                              other->rb_color = parent->rb_color;
++                              parent->rb_color = RB_BLACK;
++                              if (other->rb_right)
++                                      other->rb_right->rb_color = RB_BLACK;
++                              __rb_rotate_left(parent, root);
++                              node = root->rb_node;
++                              break;
++                      }
++              }
++              else
++              {
++                      other = parent->rb_left;
++                      if (other->rb_color == RB_RED)
++                      {
++                              other->rb_color = RB_BLACK;
++                              parent->rb_color = RB_RED;
++                              __rb_rotate_right(parent, root);
++                              other = parent->rb_left;
++                      }
++                      if ((!other->rb_left ||
++                           other->rb_left->rb_color == RB_BLACK)
++                          && (!other->rb_right ||
++                              other->rb_right->rb_color == RB_BLACK))
++                      {
++                              other->rb_color = RB_RED;
++                              node = parent;
++                              parent = node->rb_parent;
++                      }
++                      else
++                      {
++                              if (!other->rb_left ||
++                                  other->rb_left->rb_color == RB_BLACK)
++                              {
++                                      register struct rb_node * o_right;
++                                      if ((o_right = other->rb_right))
++                                              o_right->rb_color = RB_BLACK;
++                                      other->rb_color = RB_RED;
++                                      __rb_rotate_left(other, root);
++                                      other = parent->rb_left;
++                              }
++                              other->rb_color = parent->rb_color;
++                              parent->rb_color = RB_BLACK;
++                              if (other->rb_left)
++                                      other->rb_left->rb_color = RB_BLACK;
++                              __rb_rotate_right(parent, root);
++                              node = root->rb_node;
++                              break;
++                      }
++              }
++      }
++      if (node)
++              node->rb_color = RB_BLACK;
++}
++
++void rb_erase(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * child, * parent;
++      int color;
++
++      if (!node->rb_left)
++              child = node->rb_right;
++      else if (!node->rb_right)
++              child = node->rb_left;
++      else
++      {
++              struct rb_node * old = node, * left;
++
++              node = node->rb_right;
++              while ((left = node->rb_left))
++                      node = left;
++              child = node->rb_right;
++              parent = node->rb_parent;
++              color = node->rb_color;
++
++              if (child)
++                      child->rb_parent = parent;
++              if (parent)
++              {
++                      if (parent->rb_left == node)
++                              parent->rb_left = child;
++                      else
++                              parent->rb_right = child;
++              }
++              else
++                      root->rb_node = child;
++
++              if (node->rb_parent == old)
++                      parent = node;
++              node->rb_parent = old->rb_parent;
++              node->rb_color = old->rb_color;
++              node->rb_right = old->rb_right;
++              node->rb_left = old->rb_left;
++
++              if (old->rb_parent)
++              {
++                      if (old->rb_parent->rb_left == old)
++                              old->rb_parent->rb_left = node;
++                      else
++                              old->rb_parent->rb_right = node;
++              } else
++                      root->rb_node = node;
++
++              old->rb_left->rb_parent = node;
++              if (old->rb_right)
++                      old->rb_right->rb_parent = node;
++              goto color;
++      }
++
++      parent = node->rb_parent;
++      color = node->rb_color;
++
++      if (child)
++              child->rb_parent = parent;
++      if (parent)
++      {
++              if (parent->rb_left == node)
++                      parent->rb_left = child;
++              else
++                      parent->rb_right = child;
++      }
++      else
++              root->rb_node = child;
++
++ color:
++      if (color == RB_BLACK)
++              __rb_erase_color(child, parent, root);
++}
++#endif /* Before 2.4.11 */
++
++      /* These routines haven't made it into 2.4 (yet) */
++struct rb_node *rb_next(struct rb_node *node)
++{
++      /* If we have a right-hand child, go down and then left as far
++         as we can. */
++      if (node->rb_right) {
++              node = node->rb_right; 
++              while (node->rb_left)
++                      node=node->rb_left;
++              return node;
++      }
++
++      /* No right-hand children.  Everything down and left is
++         smaller than us, so any 'next' node must be in the general
++         direction of our parent. Go up the tree; any time the
++         ancestor is a right-hand child of its parent, keep going
++         up. First time it's a left-hand child of its parent, said
++         parent is our 'next' node. */
++      while (node->rb_parent && node == node->rb_parent->rb_right)
++              node = node->rb_parent;
++
++      return node->rb_parent;
++}
++
++struct rb_node *rb_prev(struct rb_node *node)
++{
++      if (node->rb_left) {
++              node = node->rb_left; 
++              while (node->rb_right)
++                      node=node->rb_right;
++              return node;
++      }
++      while (node->rb_parent && node == node->rb_parent->rb_left)
++              node = node->rb_parent;
++
++      return node->rb_parent;
++}
++
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
++{
++      struct rb_node *parent = victim->rb_parent;
++
++      /* Set the surrounding nodes to point to the replacement */
++      if (parent) {
++              if (victim == parent->rb_left)
++                      parent->rb_left = new;
++              else
++                      parent->rb_right = new;
++      } else {
++              root->rb_node = new;
++      }
++      if (victim->rb_left)
++              victim->rb_left->rb_parent = new;
++      if (victim->rb_right)
++              victim->rb_right->rb_parent = new;
++
++      /* Copy the pointers/colour from the victim to the replacement */
++      *new = *victim;
++}
+--- linux-2.4.21/fs/jffs2/read.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/read.c
+@@ -1,52 +1,32 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
+-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                   struct jffs2_full_dnode *fd, unsigned char *buf,
++                   int ofs, int len)
+ {
+       struct jffs2_raw_inode *ri;
+       size_t readlen;
+-      __u32 crc;
++      uint32_t crc;
+       unsigned char *decomprbuf = NULL;
+       unsigned char *readbuf = NULL;
+       int ret = 0;
+@@ -55,35 +35,41 @@
+       if (!ri)
+               return -ENOMEM;
+-      ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
++      ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
+       if (ret) {
+               jffs2_free_raw_inode(ri);
+-              printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
++              printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
+               return ret;
+       }
+       if (readlen != sizeof(*ri)) {
+               jffs2_free_raw_inode(ri);
+-              printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", 
+-                     fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
++              printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", 
++                     ref_offset(fd->raw), sizeof(*ri), readlen);
+               return -EIO;
+       }
+       crc = crc32(0, ri, sizeof(*ri)-8);
+-      D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
+-      if (crc != ri->node_crc) {
+-              printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
++      D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
++                ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
++                crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
++                je32_to_cpu(ri->offset), buf));
++      if (crc != je32_to_cpu(ri->node_crc)) {
++              printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
++                     je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
+               ret = -EIO;
+               goto out_ri;
+       }
+       /* There was a bug where we wrote hole nodes out with csize/dsize
+          swapped. Deal with it */
+-      if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) {
++      if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && 
++          je32_to_cpu(ri->csize)) {
+               ri->dsize = ri->csize;
+-              ri->csize = 0;
++              ri->csize = cpu_to_je32(0);
+       }
+-      D1(if(ofs + len > ri->dsize) {
+-              printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
++      D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
++              printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
++                     len, ofs, je32_to_cpu(ri->dsize));
+               ret = -EINVAL;
+               goto out_ri;
+       });
+@@ -100,18 +86,18 @@
+          Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy 
+          Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
+       */
+-      if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
++      if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
+               readbuf = buf;
+       } else {
+-              readbuf = kmalloc(ri->csize, GFP_KERNEL);
++              readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
+               if (!readbuf) {
+                       ret = -ENOMEM;
+                       goto out_ri;
+               }
+       }
+       if (ri->compr != JFFS2_COMPR_NONE) {
+-              if (len < ri->dsize) {
+-                      decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
++              if (len < je32_to_cpu(ri->dsize)) {
++                      decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
+                       if (!decomprbuf) {
+                               ret = -ENOMEM;
+                               goto out_readbuf;
+@@ -123,31 +109,35 @@
+               decomprbuf = readbuf;
+       }
+-      D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
+-      ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
++      D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
++                readbuf));
++      ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
++                             je32_to_cpu(ri->csize), &readlen, readbuf);
+-      if (!ret && readlen != ri->csize)
++      if (!ret && readlen != je32_to_cpu(ri->csize))
+               ret = -EIO;
+       if (ret)
+               goto out_decomprbuf;
+-      crc = crc32(0, readbuf, ri->csize);
+-      if (crc != ri->data_crc) {
+-              printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
++      crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
++      if (crc != je32_to_cpu(ri->data_crc)) {
++              printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
++                     je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
+               ret = -EIO;
+               goto out_decomprbuf;
+       }
+       D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
+       if (ri->compr != JFFS2_COMPR_NONE) {
+-              D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); 
+-              ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
++              D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
++                        je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); 
++              ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+               if (ret) {
+                       printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+                       goto out_decomprbuf;
+               }
+       }
+-      if (len < ri->dsize) {
++      if (len < je32_to_cpu(ri->dsize)) {
+               memcpy(buf, decomprbuf+ofs, len);
+       }
+  out_decomprbuf:
+@@ -161,3 +151,66 @@
+       return ret;
+ }
++
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                         unsigned char *buf, uint32_t offset, uint32_t len)
++{
++      uint32_t end = offset + len;
++      struct jffs2_node_frag *frag;
++      int ret;
++
++      D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
++                f->inocache->ino, offset, offset+len));
++
++      frag = jffs2_lookup_node_frag(&f->fragtree, offset);
++
++      /* XXX FIXME: Where a single physical node actually shows up in two
++         frags, we read it twice. Don't do that. */
++      /* Now we're pointing at the first frag which overlaps our page */
++      while(offset < end) {
++              D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
++              if (unlikely(!frag || frag->ofs > offset)) {
++                      uint32_t holesize = end - offset;
++                      if (frag) {
++                              D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
++                              holesize = min(holesize, frag->ofs - offset);
++                              D2(jffs2_print_frag_list(f));
++                      }
++                      D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
++                      memset(buf, 0, holesize);
++                      buf += holesize;
++                      offset += holesize;
++                      continue;
++              } else if (unlikely(!frag->node)) {
++                      uint32_t holeend = min(end, frag->ofs + frag->size);
++                      D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
++                      memset(buf, 0, holeend - offset);
++                      buf += holeend - offset;
++                      offset = holeend;
++                      frag = frag_next(frag);
++                      continue;
++              } else {
++                      uint32_t readlen;
++                      uint32_t fragofs; /* offset within the frag to start reading */
++                      
++                      fragofs = offset - frag->ofs;
++                      readlen = min(frag->size - fragofs, end - offset);
++                      D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
++                                frag->ofs+fragofs, frag->ofs+fragofs+readlen,
++                                ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
++                      ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
++                      D2(printk(KERN_DEBUG "node read done\n"));
++                      if (ret) {
++                              D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
++                              memset(buf, 0, readlen);
++                              return ret;
++                      }
++                      buf += readlen;
++                      offset += readlen;
++                      frag = frag_next(frag);
++                      D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
++              }
++      }
++      return 0;
++}
++
+--- linux-2.4.21/fs/jffs2/readinode.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/readinode.c
+@@ -1,79 +1,124 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+-/* Given an inode, probably with existing list of fragments, add the new node
+- * to the fragment list.
+- */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
+-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
++#if CONFIG_JFFS2_FS_DEBUG >= 2
++static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+ {
+-      struct jffs2_node_frag *this = f->fraglist;
++      struct jffs2_node_frag *this = frag_first(list);
++      uint32_t lastofs = 0;
++      int buggy = 0;
+       while(this) {
+               if (this->node)
+-                      printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
++                      printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
++                             this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
++                             this, frag_left(this), frag_right(this), frag_parent(this));
+               else 
+-                      printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
+-              this = this->next;
++                      printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, 
++                             this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
++              if (this->ofs != lastofs)
++                      buggy = 1;
++              lastofs = this->ofs+this->size;
++              this = frag_next(this);
+       }
+-      if (f->metadata) {
+-              printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
++      if (buggy && !permitbug) {
++              printk(KERN_CRIT "Frag tree got a hole in it\n");
++              BUG();
+       }
+-})
++}
++void jffs2_print_frag_list(struct jffs2_inode_info *f)
++{
++      jffs2_print_fragtree(&f->fragtree, 0);
+-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
++      if (f->metadata) {
++              printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
++      }
++}
++#endif
++
++#if CONFIG_JFFS2_FS_DEBUG >= 1
++static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
+ {
+-      int ret;
+-      D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
++      struct jffs2_node_frag *frag;
++      int bitched = 0;
+-      ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
++      for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+-      D2(jffs2_print_frag_list(f));
+-      return ret;
++              struct jffs2_full_dnode *fn = frag->node;
++              if (!fn || !fn->raw)
++                      continue;
++
++              if (ref_flags(fn->raw) == REF_PRISTINE) {
++
++                      if (fn->frags > 1) {
++                              printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
++                              bitched = 1;
++                      }
++                      /* A hole node which isn't multi-page should be garbage-collected
++                         and merged anyway, so we just check for the frag size here,
++                         rather than mucking around with actually reading the node
++                         and checking the compression type, which is the real way
++                         to tell a hole node. */
++                      if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
++                              printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
++                                     ref_offset(fn->raw));
++                              bitched = 1;
++                      }
++
++                      if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
++                              printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
++                                     ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
++                              bitched = 1;
++                      }
++              }
++      }
++      
++      if (bitched) {
++              struct jffs2_node_frag *thisfrag;
++
++              printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
++              thisfrag = frag_first(&f->fragtree);
++              while (thisfrag) {
++                      if (!thisfrag->node) {
++                              printk("Frag @0x%x-0x%x; node-less hole\n",
++                                     thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++                      } else if (!thisfrag->node->raw) {
++                              printk("Frag @0x%x-0x%x; raw-less hole\n",
++                                     thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++                      } else {
++                              printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
++                                     thisfrag->ofs, thisfrag->size + thisfrag->ofs,
++                                     ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
++                                     thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
++                      }
++                      thisfrag = frag_next(thisfrag);
++              }
++      }
++      return bitched;
+ }
++#endif /* D1 */
+ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
+ {
+@@ -82,42 +127,38 @@
+               if (!this->node->frags) {
+                       /* The node has no valid frags left. It's totally obsoleted */
+                       D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
+-                                this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
++                                ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
+                       jffs2_mark_node_obsolete(c, this->node->raw);
+                       jffs2_free_full_dnode(this->node);
+               } else {
+-                      D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
+-                                this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
++                      D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
++                                ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
+                                 this->node->frags));
++                      mark_ref_normal(this->node->raw);
+               }
+               
+       }
+       jffs2_free_node_frag(this);
+ }
+-/* Doesn't set inode->i_size */
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
++/* Given an inode, probably with existing list of fragments, add the new node
++ * to the fragment list.
++ */
++int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
++      int ret;
++      struct jffs2_node_frag *newfrag;
+       
+-      struct jffs2_node_frag *this, **prev, *old;
+-      struct jffs2_node_frag *newfrag, *newfrag2;
+-      __u32 lastend = 0;
+-
++      D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+       newfrag = jffs2_alloc_node_frag();
+-      if (!newfrag) {
++      if (unlikely(!newfrag))
+               return -ENOMEM;
+-      }
+-      D2(if (fn->raw)
+-              printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
+-      else
+-              printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+-      
+-      prev = list;
+-      this = *list;
++      D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++                fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
+-      if (!fn->size) {
++      if (unlikely(!fn->size)) {
+               jffs2_free_node_frag(newfrag);
+               return 0;
+       }
+@@ -126,176 +167,358 @@
+       newfrag->size = fn->size;
+       newfrag->node = fn;
+       newfrag->node->frags = 1;
+-      newfrag->next = (void *)0xdeadbeef;
++
++      ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
++      if (ret)
++              return ret;
++
++      /* If we now share a page with other nodes, mark either previous
++         or next node REF_NORMAL, as appropriate.  */
++      if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
++              struct jffs2_node_frag *prev = frag_prev(newfrag);
++
++              mark_ref_normal(fn->raw);
++              /* If we don't start at zero there's _always_ a previous */     
++              if (prev->node)
++                      mark_ref_normal(prev->node->raw);
++      }
++
++      if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
++              struct jffs2_node_frag *next = frag_next(newfrag);
++              
++              if (next) {
++                      mark_ref_normal(fn->raw);
++                      if (next->node)
++                              mark_ref_normal(next->node->raw);
++              }
++      }
++      D2(if (jffs2_sanitycheck_fragtree(f)) {
++                 printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++                        fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
++                 return 0;
++         })
++      D2(jffs2_print_frag_list(f));
++      return 0;
++}
++
++/* Doesn't set inode->i_size */
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
++{
++      struct jffs2_node_frag *this;
++      uint32_t lastend;
+       /* Skip all the nodes which are completed before this one starts */
+-      while(this && fn->ofs >= this->ofs+this->size) {
+-              lastend = this->ofs + this->size;
++      this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+-              D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
+-                        this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+-              prev = &this->next;
+-              this = this->next;
++      if (this) {
++              D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
++                        this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
++              lastend = this->ofs + this->size;
++      } else {
++              D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
++              lastend = 0;
+       }
+       /* See if we ran off the end of the list */
+-      if (!this) {
++      if (lastend <= newfrag->ofs) {
+               /* We did */
+-              if (lastend < fn->ofs) {
++
++              /* Check if 'this' node was on the same page as the new node.
++                 If so, both 'this' and the new node get marked REF_NORMAL so
++                 the GC can take a look.
++              */
++              if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
++                      if (this->node)
++                              mark_ref_normal(this->node->raw);
++                      mark_ref_normal(newfrag->node->raw);
++              }
++
++              if (lastend < newfrag->node->ofs) {
+                       /* ... and we need to put a hole in before the new node */
+                       struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
+-                      if (!holefrag)
++                      if (!holefrag) {
++                              jffs2_free_node_frag(newfrag);
+                               return -ENOMEM;
++                      }
+                       holefrag->ofs = lastend;
+-                      holefrag->size = fn->ofs - lastend;
+-                      holefrag->next = NULL;
++                      holefrag->size = newfrag->node->ofs - lastend;
+                       holefrag->node = NULL;
+-                      *prev = holefrag;
+-                      prev = &holefrag->next;
++                      if (this) {
++                              /* By definition, the 'this' node has no right-hand child, 
++                                 because there are no frags with offset greater than it.
++                                 So that's where we want to put the hole */
++                              D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
++                              rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
++                      } else {
++                              D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
++                              rb_link_node(&holefrag->rb, NULL, &list->rb_node);
+               }
+-              newfrag->next = NULL;
+-              *prev = newfrag;
++                      rb_insert_color(&holefrag->rb, list);
++                      this = holefrag;
++              }
++              if (this) {
++                      /* By definition, the 'this' node has no right-hand child, 
++                         because there are no frags with offset greater than it.
++                         So that's where we want to put the hole */
++                      D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
++                      rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);                      
++              } else {
++                      D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
++                      rb_link_node(&newfrag->rb, NULL, &list->rb_node);
++              }
++              rb_insert_color(&newfrag->rb, list);
+               return 0;
+       }
+-      D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
+-                this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
++      D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", 
++                this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
+-      /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
+-       * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs  
++      /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
++       * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs  
+        */
+-      if (fn->ofs > this->ofs) {
++      if (newfrag->ofs > this->ofs) {
+               /* This node isn't completely obsoleted. The start of it remains valid */
+-              if (this->ofs + this->size > fn->ofs + fn->size) {
++
++              /* Mark the new node and the partially covered node REF_NORMAL -- let
++                 the GC take a look at them */
++              mark_ref_normal(newfrag->node->raw);
++              if (this->node)
++                      mark_ref_normal(this->node->raw);
++
++              if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
+                       /* The new node splits 'this' frag into two */
+-                      newfrag2 = jffs2_alloc_node_frag();
++                      struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
+                       if (!newfrag2) {
+                               jffs2_free_node_frag(newfrag);
+                               return -ENOMEM;
+                       }
+-                      D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
++                      D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+                       if (this->node)
+-                              printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
++                              printk("phys 0x%08x\n", ref_offset(this->node->raw));
+                       else 
+                               printk("hole\n");
+                          )
+-                      newfrag2->ofs = fn->ofs + fn->size;
++                      
++                      /* New second frag pointing to this's node */
++                      newfrag2->ofs = newfrag->ofs + newfrag->size;
+                       newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
+-                      newfrag2->next = this->next;
+                       newfrag2->node = this->node;
+                       if (this->node)
+                               this->node->frags++;
+-                      newfrag->next = newfrag2;
+-                      this->next = newfrag;
++
++                      /* Adjust size of original 'this' */
+                       this->size = newfrag->ofs - this->ofs;
++
++                      /* Now, we know there's no node with offset
++                         greater than this->ofs but smaller than
++                         newfrag2->ofs or newfrag->ofs, for obvious
++                         reasons. So we can do a tree insert from
++                         'this' to insert newfrag, and a tree insert
++                         from newfrag to insert newfrag2. */
++                      jffs2_fragtree_insert(newfrag, this);
++                      rb_insert_color(&newfrag->rb, list);
++                      
++                      jffs2_fragtree_insert(newfrag2, newfrag);
++                      rb_insert_color(&newfrag2->rb, list);
++                      
+                       return 0;
+               }
+               /* New node just reduces 'this' frag in size, doesn't split it */
+-              this->size = fn->ofs - this->ofs;
+-              newfrag->next = this->next;
+-              this->next = newfrag;
+-              this = newfrag->next;
++              this->size = newfrag->ofs - this->ofs;
++
++              /* Again, we know it lives down here in the tree */
++              jffs2_fragtree_insert(newfrag, this);
++              rb_insert_color(&newfrag->rb, list);
+       } else {
+-              D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
+-              *prev = newfrag;
+-              newfrag->next = this;
++              /* New frag starts at the same point as 'this' used to. Replace 
++                 it in the tree without doing a delete and insertion */
++              D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
++                        newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
++                        this, this->ofs, this->ofs+this->size));
++      
++              rb_replace_node(&this->rb, &newfrag->rb, list);
++              
++              if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
++                      D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
++                      jffs2_obsolete_node_frag(c, this);
++              } else {
++                      this->ofs += newfrag->size;
++                      this->size -= newfrag->size;
++
++                      jffs2_fragtree_insert(this, newfrag);
++                      rb_insert_color(&this->rb, list);
++                      return 0;
+       }
+-      /* OK, now we have newfrag added in the correct place in the list, but
+-         newfrag->next points to a fragment which may be overlapping it
++      }
++      /* OK, now we have newfrag added in the correct place in the tree, but
++         frag_next(newfrag) may be a fragment which is overlapped by it 
+       */
+-      while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+-              /* 'this' frag is obsoleted. */
+-              old = this;
+-              this = old->next;
+-              jffs2_obsolete_node_frag(c, old);
++      while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
++              /* 'this' frag is obsoleted completely. */
++              D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
++              rb_erase(&this->rb, list);
++              jffs2_obsolete_node_frag(c, this);
+       }
+       /* Now we're pointing at the first frag which isn't totally obsoleted by 
+          the new frag */
+-      newfrag->next = this;
+       if (!this || newfrag->ofs + newfrag->size == this->ofs) {
+               return 0;
+       }
+-      /* Still some overlap */
++      /* Still some overlap but we don't need to move it in the tree */
+       this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
+       this->ofs = newfrag->ofs + newfrag->size;
++
++      /* And mark them REF_NORMAL so the GC takes a look at them */
++      if (this->node)
++              mark_ref_normal(this->node->raw);
++      mark_ref_normal(newfrag->node->raw);
++
+       return 0;
+ }
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+ {
++      struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
++
+       D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
+-      while (*list) {
+-              if ((*list)->ofs >= size) {
+-                      struct jffs2_node_frag *this = *list;
+-                      *list = this->next;
+-                      D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
+-                      jffs2_obsolete_node_frag(c, this);
+-                      continue;
+-              } else if ((*list)->ofs + (*list)->size > size) {
+-                      D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
+-                      (*list)->size = size - (*list)->ofs;
++      /* We know frag->ofs <= size. That's what lookup does for us */
++      if (frag && frag->ofs != size) {
++              if (frag->ofs+frag->size >= size) {
++                      D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++                      frag->size = size - frag->ofs;
+               }
+-              list = &(*list)->next;
++              frag = frag_next(frag);
++      }
++      while (frag && frag->ofs >= size) {
++              struct jffs2_node_frag *next = frag_next(frag);
++
++              D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++              frag_erase(frag, list);
++              jffs2_obsolete_node_frag(c, frag);
++              frag = next;
+       }
+ }
+ /* Scan the list of all nodes present for this ino, build map of versions, etc. */
+-void jffs2_read_inode (struct inode *inode)
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
++                                      struct jffs2_inode_info *f,
++                                      struct jffs2_raw_inode *latest_node);
++
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
++                      uint32_t ino, struct jffs2_raw_inode *latest_node)
+ {
+-      struct jffs2_tmp_dnode_info *tn_list, *tn;
+-      struct jffs2_full_dirent *fd_list;
+-      struct jffs2_inode_info *f;
+-      struct jffs2_full_dnode *fn = NULL;
+-      struct jffs2_sb_info *c;
+-      struct jffs2_raw_inode latest_node;
+-      __u32 latest_mctime, mctime_ver;
+-      __u32 mdata_ver = 0;
+-      int ret;
+-      ssize_t retlen;
++      D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+-      D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++ retry_inocache:
++      spin_lock(&c->inocache_lock);
++      f->inocache = jffs2_get_ino_cache(c, ino);
+-      f = JFFS2_INODE_INFO(inode);
+-      c = JFFS2_SB_INFO(inode->i_sb);
++      D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+-      memset(f, 0, sizeof(*f));
+-      D2(printk(KERN_DEBUG "getting inocache\n"));
+-      init_MUTEX(&f->sem);
+-      f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
+-      D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
++      if (f->inocache) {
++              /* Check its state. We may need to wait before we can use it */
++              switch(f->inocache->state) {
++              case INO_STATE_UNCHECKED:
++              case INO_STATE_CHECKEDABSENT:
++                      f->inocache->state = INO_STATE_READING;
++                      break;
+-      if (!f->inocache && inode->i_ino == 1) {
++              case INO_STATE_CHECKING:
++              case INO_STATE_GC:
++                      /* If it's in either of these states, we need
++                         to wait for whoever's got it to finish and
++                         put it back. */
++                      D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
++                                ino, f->inocache->state));
++                      sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++                      goto retry_inocache;
++
++              case INO_STATE_READING:
++              case INO_STATE_PRESENT:
++                      /* Eep. This should never happen. It can
++                      happen if Linux calls read_inode() again
++                      before clear_inode() has finished though. */
++                      printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
++                      /* Fail. That's probably better than allowing it to succeed */
++                      f->inocache = NULL;
++                      break;
++
++              default:
++                      BUG();
++              }
++      }
++      spin_unlock(&c->inocache_lock);
++
++      if (!f->inocache && ino == 1) {
+               /* Special case - no root inode on medium */
+               f->inocache = jffs2_alloc_inode_cache();
+               if (!f->inocache) {
+-                      printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
+-                      make_bad_inode(inode);
+-                      return;
++                      printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
++                      return -ENOMEM;
+               }
+-              D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
++              D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
+               memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+               f->inocache->ino = f->inocache->nlink = 1;
+               f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
++              f->inocache->state = INO_STATE_READING;
+               jffs2_add_ino_cache(c, f->inocache);
+       }
+       if (!f->inocache) {
+-              printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
+-              make_bad_inode(inode);
+-              return;
++              printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
++              return -ENOENT;
+       }
+-      D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
+-      inode->i_nlink = f->inocache->nlink;
++
++      return jffs2_do_read_inode_internal(c, f, latest_node);
++}
++
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++      struct jffs2_raw_inode n;
++      struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
++      int ret;
++
++      if (!f)
++              return -ENOMEM;
++
++      memset(f, 0, sizeof(*f));
++      init_MUTEX_LOCKED(&f->sem);
++      f->inocache = ic;
++
++      ret = jffs2_do_read_inode_internal(c, f, &n);
++      if (!ret) {
++              up(&f->sem);
++              jffs2_do_clear_inode(c, f);
++      }
++      kfree (f);
++      return ret;
++}
++
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
++                                      struct jffs2_inode_info *f,
++                                      struct jffs2_raw_inode *latest_node)
++{
++      struct jffs2_tmp_dnode_info *tn_list, *tn;
++      struct jffs2_full_dirent *fd_list;
++      struct jffs2_full_dnode *fn = NULL;
++      uint32_t crc;
++      uint32_t latest_mctime, mctime_ver;
++      uint32_t mdata_ver = 0;
++      size_t retlen;
++      int ret;
++
++      D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
+       /* Grab all nodes relevant to this ino */
+-      ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
++      ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+       if (ret) {
+-              printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
+-              make_bad_inode(inode);
+-              return;
++              printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
++              if (f->inocache->state == INO_STATE_READING)
++                      jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++              return ret;
+       }
+       f->dents = fd_list;
+@@ -304,219 +527,217 @@
+               fn = tn->fn;
+-              if (f->metadata && tn->version > mdata_ver) {
+-                      D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
++              if (f->metadata) {
++                      if (likely(tn->version >= mdata_ver)) {
++                              D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
+                       jffs2_mark_node_obsolete(c, f->metadata->raw);
+                       jffs2_free_full_dnode(f->metadata);
+                       f->metadata = NULL;
+                       
+                       mdata_ver = 0;
++                      } else {
++                              /* This should never happen. */
++                              printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
++                                        ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
++                              jffs2_mark_node_obsolete(c, fn->raw);
++                              jffs2_free_full_dnode(fn);
++                              /* Fill in latest_node from the metadata, not this one we're about to free... */
++                              fn = f->metadata;
++                              goto next_tn;
++                      }
+               }
+               if (fn->size) {
+                       jffs2_add_full_dnode_to_inode(c, f, fn);
+               } else {
+                       /* Zero-sized node at end of version list. Just a metadata update */
+-                      D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
++                      D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
+                       f->metadata = fn;
+                       mdata_ver = tn->version;
+               }
++      next_tn:
+               tn_list = tn->next;
+               jffs2_free_tmp_dnode_info(tn);
+       }
++      D1(jffs2_sanitycheck_fragtree(f));
++
+       if (!fn) {
+               /* No data nodes for this inode. */
+-              if (inode->i_ino != 1) {
+-                      printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
++              if (f->inocache->ino != 1) {
++                      printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
+                       if (!fd_list) {
+-                              make_bad_inode(inode);
+-                              return;
++                              if (f->inocache->state == INO_STATE_READING)
++                                      jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++                              return -EIO;
+                       }
+-                      printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
++                      printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
+               }
+-              inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
+-              latest_node.version = 0;
+-              inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+-              inode->i_nlink = f->inocache->nlink;
+-              inode->i_size = 0;
+-      } else {
+-              __u32 crc;
+-
+-              ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
+-              if (ret || retlen != sizeof(latest_node)) {
+-                      printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
+-                             ret, (long)retlen, sizeof(latest_node));
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
++              latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
++              latest_node->version = cpu_to_je32(0);
++              latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
++              latest_node->isize = cpu_to_je32(0);
++              latest_node->gid = cpu_to_je16(0);
++              latest_node->uid = cpu_to_je16(0);
++              if (f->inocache->state == INO_STATE_READING)
++                      jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++              return 0;
+               }
+-              crc = crc32(0, &latest_node, sizeof(latest_node)-8);
+-              if (crc != latest_node.node_crc) {
+-                      printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
++      ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
++      if (ret || retlen != sizeof(*latest_node)) {
++              printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
++                     ret, retlen, sizeof(*latest_node));
++              /* FIXME: If this fails, there seems to be a memory leak. Find it. */
++              up(&f->sem);
++              jffs2_do_clear_inode(c, f);
++              return ret?ret:-EIO;
+               }
+-              inode->i_mode = latest_node.mode;
+-              inode->i_uid = latest_node.uid;
+-              inode->i_gid = latest_node.gid;
+-              inode->i_size = latest_node.isize;
+-              if (S_ISREG(inode->i_mode))
+-                      jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
+-              inode->i_atime = latest_node.atime;
+-              inode->i_mtime = latest_node.mtime;
+-              inode->i_ctime = latest_node.ctime;
++      crc = crc32(0, latest_node, sizeof(*latest_node)-8);
++      if (crc != je32_to_cpu(latest_node->node_crc)) {
++              printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
++              up(&f->sem);
++              jffs2_do_clear_inode(c, f);
++              return -EIO;
+       }
+-      /* OK, now the special cases. Certain inode types should
+-         have only one data node, and it's kept as the metadata
+-         node */
+-      if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
+-          S_ISLNK(inode->i_mode)) {
+-              if (f->metadata) {
+-                      printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
+-              }
+-              if (!f->fraglist) {
+-                      printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
+-              }
+-              /* ASSERT: f->fraglist != NULL */
+-              if (f->fraglist->next) {
+-                      printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
+-                      /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
+-              }
+-              /* OK. We're happy */
+-              f->metadata = f->fraglist->node;
+-              jffs2_free_node_frag(f->fraglist);
+-              f->fraglist = NULL;
++      switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
++      case S_IFDIR:
++              if (mctime_ver > je32_to_cpu(latest_node->version)) {
++                      /* The times in the latest_node are actually older than
++                         mctime in the latest dirent. Cheat. */
++                      latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+       }                       
++              break;
+           
+-      inode->i_blksize = PAGE_SIZE;
+-      inode->i_blocks = (inode->i_size + 511) >> 9;
+       
+-      switch (inode->i_mode & S_IFMT) {
+-              unsigned short rdev;
++      case S_IFREG:
++              /* If it was a regular file, truncate it to the latest node's isize */
++              jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
++              break;
+       case S_IFLNK:
+-              inode->i_op = &jffs2_symlink_inode_operations;
+               /* Hack to work around broken isize in old symlink code.
+                  Remove this when dwmw2 comes to his senses and stops
+                  symlinks from being an entirely gratuitous special
+                  case. */
+-              if (!inode->i_size)
+-                      inode->i_size = latest_node.dsize;
+-              break;
++              if (!je32_to_cpu(latest_node->isize))
++                      latest_node->isize = latest_node->dsize;
+               
+-      case S_IFDIR:
+-              if (mctime_ver > latest_node.version) {
+-                      /* The times in the latest_node are actually older than
+-                         mctime in the latest dirent. Cheat. */
+-                      inode->i_mtime = inode->i_ctime = inode->i_atime = 
+-                              latest_mctime;
++              if (f->inocache->state != INO_STATE_CHECKING) {
++                      /* Symlink's inode data is the target path. Read it and
++                       * keep in RAM to facilitate quick follow symlink operation.
++                       * We use f->dents field to store the target path, which
++                       * is somewhat ugly. */
++                      f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
++                      if (!f->dents) {
++                              printk(KERN_WARNING "Can't allocate %d bytes of memory "
++                                              "for the symlink target path cache\n",
++                                              je32_to_cpu(latest_node->csize));
++                              up(&f->sem);
++                              jffs2_do_clear_inode(c, f);
++                              return -ENOMEM;
+               }
+-              inode->i_op = &jffs2_dir_inode_operations;
+-              inode->i_fop = &jffs2_dir_operations;
+-              break;
+-      case S_IFREG:
+-              inode->i_op = &jffs2_file_inode_operations;
+-              inode->i_fop = &jffs2_file_operations;
+-              inode->i_mapping->a_ops = &jffs2_file_address_operations;
+-              inode->i_mapping->nrpages = 0;
+-              break;
++                      ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
++                                              je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
++                      
++                      if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
++                              if (retlen != je32_to_cpu(latest_node->csize))
++                                      ret = -EIO;
++                              kfree(f->dents);
++                              f->dents = NULL;
++                              up(&f->sem);
++                              jffs2_do_clear_inode(c, f);
++                              return -ret;
++                      }
++
++                      ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
++                      D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
++                                              (char *)f->dents));
++              }
++              
++              /* fall through... */
+       case S_IFBLK:
+       case S_IFCHR:
+-              /* Read the device numbers from the media */
+-              D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+-              if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+-                      /* Eep */
+-                      printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+-                      jffs2_clear_inode(inode);
+-                      make_bad_inode(inode);
+-                      return;
++              /* Certain inode types should have only one data node, and it's
++                 kept as the metadata node */
++              if (f->metadata) {
++                      printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
++                             f->inocache->ino, jemode_to_cpu(latest_node->mode));
++                      up(&f->sem);
++                      jffs2_do_clear_inode(c, f);
++                      return -EIO;
+               }                       
+-
+-      case S_IFSOCK:
+-      case S_IFIFO:
+-              inode->i_op = &jffs2_file_inode_operations;
+-              init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
++              if (!frag_first(&f->fragtree)) {
++                      printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
++                             f->inocache->ino, jemode_to_cpu(latest_node->mode));
++                      up(&f->sem);
++                      jffs2_do_clear_inode(c, f);
++                      return -EIO;
++              }
++              /* ASSERT: f->fraglist != NULL */
++              if (frag_next(frag_first(&f->fragtree))) {
++                      printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
++                             f->inocache->ino, jemode_to_cpu(latest_node->mode));
++                      /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
++                      up(&f->sem);
++                      jffs2_do_clear_inode(c, f);
++                      return -EIO;
++              }
++              /* OK. We're happy */
++              f->metadata = frag_first(&f->fragtree)->node;
++              jffs2_free_node_frag(frag_first(&f->fragtree));
++              f->fragtree = RB_ROOT;
+               break;
+-
+-      default:
+-              printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
+       }
+-      D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++      if (f->inocache->state == INO_STATE_READING)
++              jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++
++      return 0;
+ }
+-void jffs2_clear_inode (struct inode *inode)
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
+ {
+-      /* We can forget about this inode for now - drop all 
+-       *  the nodelists associated with it, etc.
+-       */
+-      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      struct jffs2_node_frag *frag, *frags;
+       struct jffs2_full_dirent *fd, *fds;
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+-        /* I don't think we care about the potential race due to reading this
+-           without f->sem. It can never get undeleted. */
+-        int deleted = f->inocache && !f->inocache->nlink;
+-
+-      D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+-
+-      /* If it's a deleted inode, grab the alloc_sem. This prevents
+-         jffs2_garbage_collect_pass() from deciding that it wants to
+-         garbage collect one of the nodes we're just about to mark 
+-         obsolete -- by the time we drop alloc_sem and return, all
+-         the nodes are marked obsolete, and jffs2_g_c_pass() won't
+-         call iget() for the inode in question.
+-      */
+-      if (deleted)
+-              down(&c->alloc_sem);
++      int deleted;
+       down(&f->sem);
++      deleted = f->inocache && !f->inocache->nlink;
++
++      if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
++              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
+-      frags = f->fraglist;
+-      fds = f->dents;
+       if (f->metadata) {
+               if (deleted)
+                       jffs2_mark_node_obsolete(c, f->metadata->raw);
+               jffs2_free_full_dnode(f->metadata);
+       }
+-      while (frags) {
+-              frag = frags;
+-              frags = frag->next;
+-              D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
+-
+-              if (frag->node && !(--frag->node->frags)) {
+-                      /* Not a hole, and it's the final remaining frag of this node. Free the node */
+-                      if (deleted)
+-                              jffs2_mark_node_obsolete(c, frag->node->raw);
++      jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
+-                      jffs2_free_full_dnode(frag->node);
+-              }
+-              jffs2_free_node_frag(frag);
++      /* For symlink inodes we us f->dents to store the target path name */
++      if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
++              if (f->dents) {
++                      kfree(f->dents);
++                      f->dents = NULL;
+       }
++      } else {
++              fds = f->dents;
++
+       while(fds) {
+               fd = fds;
+               fds = fd->next;
+               jffs2_free_full_dirent(fd);
+       }
++      }
+-      up(&f->sem);
+-
+-      if(deleted)
+-              up(&c->alloc_sem);
+-};
++      if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
++              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++              if (f->inocache->nodes == (void *)f->inocache)
++                      jffs2_del_ino_cache(c, f->inocache);
++      }
++      up(&f->sem);
++}
+--- linux-2.4.21/fs/jffs2/scan.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/scan.c
+@@ -1,47 +1,25 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/pagemap.h>
++#include <linux/crc32.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#define DEFAULT_EMPTY_SCAN_SIZE 1024
+ #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+               c->free_size -= _x; c->dirty_size += _x; \
+@@ -51,6 +29,10 @@
+               c->free_size -= _x; c->used_size += _x; \
+               jeb->free_size -= _x ; jeb->used_size += _x; \
+               }while(0)
++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
++              c->free_size -= _x; c->unchecked_size += _x; \
++              jeb->free_size -= _x ; jeb->unchecked_size += _x; \
++              }while(0)
+ #define noisy_printk(noise, args...) do { \
+       if (*(noise)) { \
+@@ -63,39 +45,96 @@
+ } while(0)
+ static uint32_t pseudo_random;
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c);
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++                                unsigned char *buf, uint32_t buf_size);
+ /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. 
+  * Returning an error will abort the mount - bad checksums etc. should just mark the space
+  * as dirty.
+  */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
++                               struct jffs2_raw_inode *ri, uint32_t ofs);
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++                               struct jffs2_raw_dirent *rd, uint32_t ofs);
++
++#define BLK_STATE_ALLFF               0
++#define BLK_STATE_CLEAN               1
++#define BLK_STATE_PARTDIRTY   2
++#define BLK_STATE_CLEANMARKER 3
++#define BLK_STATE_ALLDIRTY    4
++#define BLK_STATE_BADBLOCK    5
++
++static inline int min_free(struct jffs2_sb_info *c)
++{
++      uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
++              return c->wbuf_pagesize;
++#endif
++      return min;
++}
++
++static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
++      if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
++              return sector_size;
++      else
++              return DEFAULT_EMPTY_SCAN_SIZE;
++}
+ int jffs2_scan_medium(struct jffs2_sb_info *c)
+ {
+       int i, ret;
+-      __u32 empty_blocks = 0;
++      uint32_t empty_blocks = 0, bad_blocks = 0;
++      unsigned char *flashbuf = NULL;
++      uint32_t buf_size = 0;
++#ifndef __ECOS
++      size_t pointlen;
+-      if (!c->blocks) {
+-              printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+-              return -EINVAL;
++      if (c->mtd->point) {
++              ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
++              if (!ret && pointlen < c->mtd->size) {
++                      /* Don't muck about if it won't let us point to the whole flash */
++                      D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
++                      c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++                      flashbuf = NULL;
++              }
++              if (ret)
++                      D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
++      }
++#endif
++      if (!flashbuf) {
++              /* For NAND it's quicker to read a whole eraseblock at a time,
++                 apparently */
++              if (jffs2_cleanmarker_oob(c))
++                      buf_size = c->sector_size;
++              else
++                      buf_size = PAGE_SIZE;
++
++              /* Respect kmalloc limitations */
++              if (buf_size > 128*1024)
++                      buf_size = 128*1024;
++
++              D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
++              flashbuf = kmalloc(buf_size, GFP_KERNEL);
++              if (!flashbuf)
++                      return -ENOMEM;
+       }
++
+       for (i=0; i<c->nr_blocks; i++) {
+               struct jffs2_eraseblock *jeb = &c->blocks[i];
+-              ret = jffs2_scan_eraseblock(c, jeb);
++              ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
++
+               if (ret < 0)
+-                      return ret;
++                      goto out;
+               ACCT_PARANOIA_CHECK(jeb);
+               /* Now decide which list to put it on */
+-              if (ret == 1) {
++              switch(ret) {
++              case BLK_STATE_ALLFF:
+                       /* 
+                        * Empty block.   Since we can't be sure it 
+                        * was entirely erased, we just queue it for erase
+@@ -103,10 +142,12 @@
+                        * is complete.  Meanwhile we still count it as empty
+                        * for later checks.
+                        */
+-                      list_add(&jeb->list, &c->erase_pending_list);
+                       empty_blocks++;
++                      list_add(&jeb->list, &c->erase_pending_list);
+                       c->nr_erasing_blocks++;
+-              } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
++                      break;
++
++              case BLK_STATE_CLEANMARKER:
+                       /* Only a CLEANMARKER node is valid */
+                       if (!jeb->dirty_size) {
+                               /* It's actually free */
+@@ -118,74 +159,224 @@
+                               list_add(&jeb->list, &c->erase_pending_list);
+                               c->nr_erasing_blocks++;
+                       }
+-              } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
++                      break;
++
++              case BLK_STATE_CLEAN:
+                         /* Full (or almost full) of clean data. Clean list */
+                         list_add(&jeb->list, &c->clean_list);
+-                } else if (jeb->used_size) {
++                      break;
++
++              case BLK_STATE_PARTDIRTY:
+                         /* Some data, but not full. Dirty list. */
+-                        /* Except that we want to remember the block with most free space,
+-                           and stick it in the 'nextblock' position to start writing to it.
+-                           Later when we do snapshots, this must be the most recent block,
+-                           not the one with most free space.
+-                        */
+-                        if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && 
++                        /* We want to remember the block with most free space
++                           and stick it in the 'nextblock' position to start writing to it. */
++                        if (jeb->free_size > min_free(c) && 
+                                 (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+                                 /* Better candidate for the next writes to go to */
+-                                if (c->nextblock)
++                                if (c->nextblock) {
++                                      c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++                                      c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++                                      c->free_size -= c->nextblock->free_size;
++                                      c->wasted_size -= c->nextblock->wasted_size;
++                                      c->nextblock->free_size = c->nextblock->wasted_size = 0;
++                                      if (VERYDIRTY(c, c->nextblock->dirty_size)) {
++                                              list_add(&c->nextblock->list, &c->very_dirty_list);
++                                      } else {
+                                         list_add(&c->nextblock->list, &c->dirty_list);
++                                      }
++                              }
+                                 c->nextblock = jeb;
+                         } else {
++                              jeb->dirty_size += jeb->free_size + jeb->wasted_size;
++                              c->dirty_size += jeb->free_size + jeb->wasted_size;
++                              c->free_size -= jeb->free_size;
++                              c->wasted_size -= jeb->wasted_size;
++                              jeb->free_size = jeb->wasted_size = 0;
++                              if (VERYDIRTY(c, jeb->dirty_size)) {
++                                      list_add(&jeb->list, &c->very_dirty_list);
++                              } else {
+                                 list_add(&jeb->list, &c->dirty_list);
+                         }
+-              } else {
++                        }
++                      break;
++
++              case BLK_STATE_ALLDIRTY:
+                       /* Nothing valid - not even a clean marker. Needs erasing. */
+                         /* For now we just put it on the erasing list. We'll start the erases later */
+-                      printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
++                      D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
+                         list_add(&jeb->list, &c->erase_pending_list);
+                       c->nr_erasing_blocks++;
++                      break;
++                      
++              case BLK_STATE_BADBLOCK:
++                      D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
++                        list_add(&jeb->list, &c->bad_list);
++                      c->bad_size += c->sector_size;
++                      c->free_size -= c->sector_size;
++                      bad_blocks++;
++                      break;
++              default:
++                      printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
++                      BUG();  
+               }
+       }
+-      /* Rotate the lists by some number to ensure wear levelling */
+-      jffs2_rotate_lists(c);
++      /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
++      if (c->nextblock && (c->nextblock->dirty_size)) {
++              c->nextblock->wasted_size += c->nextblock->dirty_size;
++              c->wasted_size += c->nextblock->dirty_size;
++              c->dirty_size -= c->nextblock->dirty_size;
++              c->nextblock->dirty_size = 0;
++      }
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
++              /* If we're going to start writing into a block which already 
++                 contains data, and the end of the data isn't page-aligned,
++                 skip a little and align it. */
++
++              uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
++
++              D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
++                        skip));
++              c->nextblock->wasted_size += skip;
++              c->wasted_size += skip;
++
++              c->nextblock->free_size -= skip;
++              c->free_size -= skip;
++      }
++#endif
+       if (c->nr_erasing_blocks) {
+-              if (!c->used_size && empty_blocks != c->nr_blocks) {
++              if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { 
+                       printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+-                      return -EIO;
++                      printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
++                      ret = -EIO;
++                      goto out;
+               }
+               jffs2_erase_pending_trigger(c);
+       }
++      ret = 0;
++ out:
++      if (buf_size)
++              kfree(flashbuf);
++#ifndef __ECOS
++      else 
++              c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++#endif
++      return ret;
++}
++
++static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
++                              uint32_t ofs, uint32_t len)
++{
++      int ret;
++      size_t retlen;
++
++      ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
++      if (ret) {
++              D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
++              return ret;
++      }
++      if (retlen < len) {
++              D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
++              return -EIO;
++      }
++      D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
++      D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
++                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
+       return 0;
+ }
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
+-      struct jffs2_unknown_node node;
+-      __u32 ofs, prevofs;
+-      __u32 hdr_crc, nodetype;
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++                                unsigned char *buf, uint32_t buf_size) {
++      struct jffs2_unknown_node *node;
++      struct jffs2_unknown_node crcnode;
++      uint32_t ofs, prevofs;
++      uint32_t hdr_crc, buf_ofs, buf_len;
+       int err;
+       int noise = 0;
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      int cleanmarkerfound = 0;
++#endif
+       ofs = jeb->offset;
+       prevofs = jeb->offset - 1;
+       D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
+-      err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+-      if (err) return err;
+-      if (ofs == jeb->offset + c->sector_size) {
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      if (jffs2_cleanmarker_oob(c)) {
++              int ret = jffs2_check_nand_cleanmarker(c, jeb);
++              D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
++              /* Even if it's not found, we still scan to see
++                 if the block is empty. We use this information
++                 to decide whether to erase it or not. */
++              switch (ret) {
++              case 0:         cleanmarkerfound = 1; break;
++              case 1:         break;
++              case 2:         return BLK_STATE_BADBLOCK;
++              case 3:         return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
++              default:        return ret;
++              }
++      }
++#endif
++      buf_ofs = jeb->offset;
++
++      if (!buf_size) {
++              buf_len = c->sector_size;
++      } else {
++              buf_len = EMPTY_SCAN_SIZE(c->sector_size);
++              err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
++              if (err)
++                      return err;
++      }
++      
++      /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
++      ofs = 0;
++
++      /* Scan only 4KiB of 0xFF before declaring it's empty */
++      while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
++              ofs += 4;
++
++      if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++              if (jffs2_cleanmarker_oob(c)) {
++                      /* scan oob, take care of cleanmarker */
++                      int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
++                      D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
++                      switch (ret) {
++                      case 0:         return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
++                      case 1:         return BLK_STATE_ALLDIRTY;
++                      default:        return ret;
++                      }
++              }
++#endif
+               D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+-              return 1;       /* special return code */
++              if (c->cleanmarker_size == 0)
++                      return BLK_STATE_CLEANMARKER;   /* don't bother with re-erase */
++              else
++                      return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
++      }
++      if (ofs) {
++              D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
++                        jeb->offset + ofs));
++              DIRTY_SPACE(ofs);
+       }
+       
++      /* Now ofs is a complete physical flash offset as it always was... */
++      ofs += jeb->offset;
++
+       noise = 10;
++scan_more:    
+       while(ofs < jeb->offset + c->sector_size) {
+-              ssize_t retlen;
+-              ACCT_PARANOIA_CHECK(jeb);
++
++              D1(ACCT_PARANOIA_CHECK(jeb));
++
++              cond_resched();
+               
+               if (ofs & 3) {
+                       printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
+-                      ofs = (ofs+3)&~3;
++                      ofs = PAD(ofs);
+                       continue;
+               }
+               if (ofs == prevofs) {
+@@ -196,102 +387,183 @@
+               }
+               prevofs = ofs;
+               
+-              if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
+-                      D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
++              if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
++                      D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
++                                jeb->offset, c->sector_size, ofs, sizeof(*node)));
+                       DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
+                       break;
+               }
+-              err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
+-              
+-              if (err) {
+-                      D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
++              if (buf_ofs + buf_len < ofs + sizeof(*node)) {
++                      buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++                      D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
++                                sizeof(struct jffs2_unknown_node), buf_len, ofs));
++                      err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++                      if (err)
+                       return err;
++                      buf_ofs = ofs;
+               }
+-              if (retlen < sizeof(node)) {
+-                      D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
+-                      DIRTY_SPACE(retlen);
+-                      ofs += retlen;
+-                      continue;
++
++              node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
++
++              if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
++                      uint32_t inbuf_ofs;
++                      uint32_t empty_start;
++
++                      empty_start = ofs;
++                      ofs += 4;
++
++                      D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
++              more_empty:
++                      inbuf_ofs = ofs - buf_ofs;
++                      while (inbuf_ofs < buf_len) {
++                              if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
++                                      printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
++                                             empty_start, ofs);
++                                      DIRTY_SPACE(ofs-empty_start);
++                                      goto scan_more;
+               }
+-              if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
+-                      D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
+-                      err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+-                      if (err) return err;
+-                      continue;
++                              inbuf_ofs+=4;
++                              ofs += 4;
+               }
++                      /* Ran off end. */
++                      D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
+-              if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
++                      /* If we're only checking the beginning of a block with a cleanmarker,
++                         bail now */
++                      if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && 
++                          c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
++                              D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
++                              return BLK_STATE_CLEANMARKER;
++                      }
++
++                      /* See how much more there is to read in this eraseblock... */
++                      buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++                      if (!buf_len) {
++                              /* No more to read. Break out of main loop without marking 
++                                 this range of empty space as dirty (because it's not) */
++                              D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
++                                        empty_start));
++                              break;
++                      }
++                      D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
++                      err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++                      if (err)
++                              return err;
++                      buf_ofs = ofs;
++                      goto more_empty;
++              }
++
++              if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
+                       printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+-              if (node.magic == JFFS2_DIRTY_BITMASK) {
+-                      D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
++              if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
++                      D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+-              if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
++              if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
+                       printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
+                       printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+-              if (node.magic != JFFS2_MAGIC_BITMASK) {
++              if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
+                       /* OK. We're out of possibilities. Whinge and move on */
+-                      noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
++                      noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", 
++                                   JFFS2_MAGIC_BITMASK, ofs, 
++                                   je16_to_cpu(node->magic));
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+               /* We seem to have a node of sorts. Check the CRC */
+-              nodetype = node.nodetype;
+-              node.nodetype |= JFFS2_NODE_ACCURATE;
+-              hdr_crc = crc32(0, &node, sizeof(node)-4);
+-              node.nodetype = nodetype;
+-              if (hdr_crc != node.hdr_crc) {
++              crcnode.magic = node->magic;
++              crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
++              crcnode.totlen = node->totlen;
++              hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
++
++              if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
+                       noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+-                                   ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
++                                   ofs, je16_to_cpu(node->magic),
++                                   je16_to_cpu(node->nodetype), 
++                                   je32_to_cpu(node->totlen),
++                                   je32_to_cpu(node->hdr_crc),
++                                   hdr_crc);
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+-              if (ofs + node.totlen > jeb->offset + c->sector_size) {
++              if (ofs + je32_to_cpu(node->totlen) > 
++                  jeb->offset + c->sector_size) {
+                       /* Eep. Node goes over the end of the erase block. */
+                       printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+-                             ofs, node.totlen);
++                             ofs, je32_to_cpu(node->totlen));
+                       printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
+                       DIRTY_SPACE(4);
+                       ofs += 4;
+                       continue;
+               }
+-              switch(node.nodetype | JFFS2_NODE_ACCURATE) {
++              if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
++                      /* Wheee. This is an obsoleted node */
++                      D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
++                      DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++                      ofs += PAD(je32_to_cpu(node->totlen));
++                      continue;
++              }
++
++              switch(je16_to_cpu(node->nodetype)) {
+               case JFFS2_NODETYPE_INODE:
+-                      err = jffs2_scan_inode_node(c, jeb, &ofs);
++                      if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
++                              buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++                              D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
++                                        sizeof(struct jffs2_raw_inode), buf_len, ofs));
++                              err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++                              if (err)
++                                      return err;
++                              buf_ofs = ofs;
++                              node = (void *)buf;
++                      }
++                      err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
+                       if (err) return err;
++                      ofs += PAD(je32_to_cpu(node->totlen));
+                       break;
+                       
+               case JFFS2_NODETYPE_DIRENT:
+-                      err = jffs2_scan_dirent_node(c, jeb, &ofs);
++                      if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
++                              buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++                              D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
++                                        je32_to_cpu(node->totlen), buf_len, ofs));
++                              err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++                              if (err)
++                                      return err;
++                              buf_ofs = ofs;
++                              node = (void *)buf;
++                      }
++                      err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
+                       if (err) return err;
++                      ofs += PAD(je32_to_cpu(node->totlen));
+                       break;
+               case JFFS2_NODETYPE_CLEANMARKER:
+-                      if (node.totlen != sizeof(struct jffs2_unknown_node)) {
++                      D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
++                      if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
+                               printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", 
+-                                     ofs, node.totlen, sizeof(struct jffs2_unknown_node));
++                                     ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
+                               DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++                              ofs += PAD(sizeof(struct jffs2_unknown_node));
+                       } else if (jeb->first_node) {
+                               printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
+                               DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+                               ofs += PAD(sizeof(struct jffs2_unknown_node));
+-                              continue;
+                       } else {
+                               struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
+                               if (!marker_ref) {
+@@ -300,98 +572,80 @@
+                               }
+                               marker_ref->next_in_ino = NULL;
+                               marker_ref->next_phys = NULL;
+-                              marker_ref->flash_offset = ofs;
+-                              marker_ref->totlen = sizeof(struct jffs2_unknown_node);
++                              marker_ref->flash_offset = ofs | REF_NORMAL;
++                              marker_ref->__totlen = c->cleanmarker_size;
+                               jeb->first_node = jeb->last_node = marker_ref;
+                            
+-                              USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++                              USED_SPACE(PAD(c->cleanmarker_size));
++                              ofs += PAD(c->cleanmarker_size);
+                       }
+-                      ofs += PAD(sizeof(struct jffs2_unknown_node));
++                      break;
++
++              case JFFS2_NODETYPE_PADDING:
++                      DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++                      ofs += PAD(je32_to_cpu(node->totlen));
+                       break;
+               default:
+-                      switch (node.nodetype & JFFS2_COMPAT_MASK) {
++                      switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
+                       case JFFS2_FEATURE_ROCOMPAT:
+-                              printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++                              printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+                               c->flags |= JFFS2_SB_FLAG_RO;
+-                              if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
++                              if (!(jffs2_is_readonly(c)))
+                                       return -EROFS;
+-                              DIRTY_SPACE(PAD(node.totlen));
+-                              ofs += PAD(node.totlen);
+-                              continue;
++                              DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++                              ofs += PAD(je32_to_cpu(node->totlen));
++                              break;
+                       case JFFS2_FEATURE_INCOMPAT:
+-                              printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++                              printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+                               return -EINVAL;
+                       case JFFS2_FEATURE_RWCOMPAT_DELETE:
+-                              printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+-                              DIRTY_SPACE(PAD(node.totlen));
+-                              ofs += PAD(node.totlen);
++                              D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++                              DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++                              ofs += PAD(je32_to_cpu(node->totlen));
+                               break;
+                       case JFFS2_FEATURE_RWCOMPAT_COPY:
+-                              printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+-                              USED_SPACE(PAD(node.totlen));
+-                              ofs += PAD(node.totlen);
++                              D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++                              USED_SPACE(PAD(je32_to_cpu(node->totlen)));
++                              ofs += PAD(je32_to_cpu(node->totlen));
+                               break;
+                       }
+               }
+       }
+-      D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, 
+-                jeb->free_size, jeb->dirty_size, jeb->used_size));
+-      return 0;
+-}
+-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
+-{
+-      __u32 *buf;
+-      __u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
+-      __u32 curofs = *startofs;
+       
+-      buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
+-      if (!buf) {
+-              printk(KERN_WARNING "Scan buffer allocation failed\n");
+-              return -ENOMEM;
+-      }
+-      while(scanlen) {
+-              ssize_t retlen;
+-              int ret, i;
++      D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, 
++                jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
+               
+-              ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
+-              if(ret) {
+-                      D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
+-                      kfree(buf);
+-                      return ret;
+-              }
+-              if (retlen < 4) {
+-                      D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
+-                      kfree(buf);
+-                      return -EIO;
++      /* mark_node_obsolete can add to wasted !! */
++      if (jeb->wasted_size) {
++              jeb->dirty_size += jeb->wasted_size;
++              c->dirty_size += jeb->wasted_size;
++              c->wasted_size -= jeb->wasted_size;
++              jeb->wasted_size = 0;
+               }
+-              for (i=0; i<(retlen / 4); i++) {
+-                      if (buf[i] != 0xffffffff) {
+-                              curofs += i*4;
+-                              noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
+-                              DIRTY_SPACE(curofs - (*startofs));
+-                              *startofs = curofs;
+-                              kfree(buf);
+-                              return 0;
+-                      }
+-              }
+-              scanlen -= retlen&~3;
+-              curofs += retlen&~3;
+-      }
++      if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
++              && (!jeb->first_node || !jeb->first_node->next_phys) )
++              return BLK_STATE_CLEANMARKER;
+-      D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+-      kfree(buf);
+-      *startofs = curofs;
+-      return 0;
++      /* move blocks with max 4 byte dirty space to cleanlist */      
++      else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
++              c->dirty_size -= jeb->dirty_size;
++              c->wasted_size += jeb->dirty_size; 
++              jeb->wasted_size += jeb->dirty_size;
++              jeb->dirty_size = 0;
++              return BLK_STATE_CLEAN;
++      } else if (jeb->used_size || jeb->unchecked_size)
++              return BLK_STATE_PARTDIRTY;
++      else
++              return BLK_STATE_ALLDIRTY;
+ }
+-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
++static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+       struct jffs2_inode_cache *ic;
+@@ -399,137 +653,77 @@
+       if (ic)
+               return ic;
++      if (ino > c->highest_ino)
++              c->highest_ino = ino;
++
+       ic = jffs2_alloc_inode_cache();
+       if (!ic) {
+               printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+               return NULL;
+       }
+       memset(ic, 0, sizeof(*ic));
+-      ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
+-      if (!ic->scan) {
+-              printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
+-              jffs2_free_inode_cache(ic);
+-              return NULL;
+-      }
+-      memset(ic->scan, 0, sizeof(*ic->scan));
++
+       ic->ino = ino;
+       ic->nodes = (void *)ic;
+       jffs2_add_ino_cache(c, ic);
+       if (ino == 1)
+-              ic->nlink=1;
++              ic->nlink = 1;
+       return ic;
+ }
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
++                               struct jffs2_raw_inode *ri, uint32_t ofs)
+ {
+       struct jffs2_raw_node_ref *raw;
+-      struct jffs2_full_dnode *fn;
+-      struct jffs2_tmp_dnode_info *tn, **tn_list;
+       struct jffs2_inode_cache *ic;
+-      struct jffs2_raw_inode ri;
+-      __u32 crc;
+-      __u16 oldnodetype;
+-      int ret;
+-      ssize_t retlen;
+-
+-      D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+-
+-      ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
+-      if (ret) {
+-              printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+-              return ret;
+-      }
+-      if (retlen != sizeof(ri)) {
+-              printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
+-                     retlen, *ofs, sizeof(ri));
+-              return -EIO;
+-      }
+-
+-      /* We sort of assume that the node was accurate when it was 
+-         first written to the medium :) */
+-      oldnodetype = ri.nodetype;
+-      ri.nodetype |= JFFS2_NODE_ACCURATE;
+-      crc = crc32(0, &ri, sizeof(ri)-8);
+-      ri.nodetype = oldnodetype;
+-
+-      if(crc != ri.node_crc) {
+-              printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+-                     *ofs, ri.node_crc, crc);
+-              /* FIXME: Why do we believe totlen? */
+-              DIRTY_SPACE(4);
+-              *ofs += 4;
+-              return 0;
+-      }
+-      /* There was a bug where we wrote hole nodes out with csize/dsize
+-         swapped. Deal with it */
+-      if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) {
+-              ri.dsize = ri.csize;
+-              ri.csize = 0;
+-      }
++      uint32_t ino = je32_to_cpu(ri->ino);
+-      if (ri.csize) {
+-              /* Check data CRC too */
+-              unsigned char *dbuf;
+-              __u32 crc;
++      D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+-              dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+-              if (!dbuf) {
+-                      printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
+-                      return -ENOMEM;
+-              }
+-              ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
+-              if (ret) {
+-                      printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
+-                      kfree(dbuf);
+-                      return ret;
+-              }
+-              if (retlen != ri.csize) {
+-                      printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
+-                             retlen, *ofs+ sizeof(ri), ri.csize);
+-                      kfree(dbuf);
+-                      return -EIO;
+-              }
+-              crc = crc32(0, dbuf, ri.csize);
+-              kfree(dbuf);
+-              if (crc != ri.data_crc) {
+-                      printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+-                             *ofs, ri.data_crc, crc);
+-                      DIRTY_SPACE(PAD(ri.totlen));
+-                      *ofs += PAD(ri.totlen);
+-                      return 0;
+-              }
+-      }
++      /* We do very little here now. Just check the ino# to which we should attribute
++         this node; we can do all the CRC checking etc. later. There's a tradeoff here -- 
++         we used to scan the flash once only, reading everything we want from it into
++         memory, then building all our in-core data structures and freeing the extra
++         information. Now we allow the first part of the mount to complete a lot quicker,
++         but we have to go _back_ to the flash in order to finish the CRC checking, etc. 
++         Which means that the _full_ amount of time to get to proper write mode with GC
++         operational may actually be _longer_ than before. Sucks to be me. */
+-      /* Wheee. It worked */
+       raw = jffs2_alloc_raw_node_ref();
+       if (!raw) {
+               printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
+               return -ENOMEM;
+       }
+-      tn = jffs2_alloc_tmp_dnode_info();
+-      if (!tn) {
+-              jffs2_free_raw_node_ref(raw);
+-              return -ENOMEM;
+-      }
+-      fn = jffs2_alloc_full_dnode();
+-      if (!fn) {
+-              jffs2_free_tmp_dnode_info(tn);
++
++      ic = jffs2_get_ino_cache(c, ino);
++      if (!ic) {
++              /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
++                 first node we found for this inode. Do a CRC check to protect against the former
++                 case */
++              uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
++
++              if (crc != je32_to_cpu(ri->node_crc)) {
++                      printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++                             ofs, je32_to_cpu(ri->node_crc), crc);
++                      /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++                      DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+               jffs2_free_raw_node_ref(raw);
+-              return -ENOMEM;
++                      return 0;
+       }
+-      ic = jffs2_scan_make_ino_cache(c, ri.ino);
++              ic = jffs2_scan_make_ino_cache(c, ino);
+       if (!ic) {
+-              jffs2_free_full_dnode(fn);
+-              jffs2_free_tmp_dnode_info(tn);
+               jffs2_free_raw_node_ref(raw);
+               return -ENOMEM;
+       }
++      }
+-      /* Build the data structures and file them for later */
+-      raw->flash_offset = *ofs;
+-      raw->totlen = PAD(ri.totlen);
++      /* Wheee. It worked */
++
++      raw->flash_offset = ofs | REF_UNCHECKED;
++      raw->__totlen = PAD(je32_to_cpu(ri->totlen));
+       raw->next_phys = NULL;
+       raw->next_in_ino = ic->nodes;
++
+       ic->nodes = raw;
+       if (!jeb->first_node)
+               jeb->first_node = raw;
+@@ -538,134 +732,56 @@
+       jeb->last_node = raw;
+       D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", 
+-                ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
+-
+-      pseudo_random += ri.version;
+-
+-      for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
+-              if ((*tn_list)->version < ri.version)
+-                      continue;
+-              if ((*tn_list)->version > ri.version) 
+-                      break;
+-              /* Wheee. We've found another instance of the same version number.
+-                 We should obsolete one of them. 
+-              */
+-              D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
+-              if (!jeb->used_size) {
+-                      D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", 
+-                                jeb->offset, raw->flash_offset & ~3));
+-                      ri.nodetype &= ~JFFS2_NODE_ACCURATE;
+-                      /* Perhaps we could also mark it as such on the medium. Maybe later */
+-              }
+-              break;
+-      }
+-
+-      if (ri.nodetype & JFFS2_NODE_ACCURATE) {
+-              memset(fn,0,sizeof(*fn));
+-
+-              fn->ofs = ri.offset;
+-              fn->size = ri.dsize;
+-              fn->frags = 0;
+-              fn->raw = raw;
+-
+-              tn->next = NULL;
+-              tn->fn = fn;
+-              tn->version = ri.version;
+-
+-              USED_SPACE(PAD(ri.totlen));
+-              jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
+-              /* Make sure the one we just added is the _last_ in the list
+-                 with this version number, so the older ones get obsoleted */
+-              while (tn->next && tn->next->version == tn->version) {
++                je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
++                je32_to_cpu(ri->offset),
++                je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
+-                      D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
+-                                fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
++      pseudo_random += je32_to_cpu(ri->version);
+-                      if(tn->fn != fn)
+-                              BUG();
+-                      tn->fn = tn->next->fn;
+-                      tn->next->fn = fn;
+-                      tn = tn->next;
+-              }
+-      } else {
+-              jffs2_free_full_dnode(fn);
+-              jffs2_free_tmp_dnode_info(tn);
+-              raw->flash_offset |= 1;
+-              DIRTY_SPACE(PAD(ri.totlen));
+-      }               
+-      *ofs += PAD(ri.totlen);
++      UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
+       return 0;
+ }
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
++                                struct jffs2_raw_dirent *rd, uint32_t ofs)
+ {
+       struct jffs2_raw_node_ref *raw;
+       struct jffs2_full_dirent *fd;
+       struct jffs2_inode_cache *ic;
+-      struct jffs2_raw_dirent rd;
+-      __u16 oldnodetype;
+-      int ret;
+-      __u32 crc;
+-      ssize_t retlen;
+-
+-      D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
++      uint32_t crc;
+-      ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
+-      if (ret) {
+-              printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+-              return ret;
+-      }
+-      if (retlen != sizeof(rd)) {
+-              printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
+-                     retlen, *ofs, sizeof(rd));
+-              return -EIO;
+-      }
++      D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
+-      /* We sort of assume that the node was accurate when it was 
+-         first written to the medium :) */
+-      oldnodetype = rd.nodetype;
+-      rd.nodetype |= JFFS2_NODE_ACCURATE;
+-      crc = crc32(0, &rd, sizeof(rd)-8);
+-      rd.nodetype = oldnodetype;
++      /* We don't get here unless the node is still valid, so we don't have to
++         mask in the ACCURATE bit any more. */
++      crc = crc32(0, rd, sizeof(*rd)-8);
+-      if (crc != rd.node_crc) {
++      if (crc != je32_to_cpu(rd->node_crc)) {
+               printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+-                     *ofs, rd.node_crc, crc);
+-              /* FIXME: Why do we believe totlen? */
+-              DIRTY_SPACE(4);
+-              *ofs += 4;
++                     ofs, je32_to_cpu(rd->node_crc), crc);
++              /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++              DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+               return 0;
+       }
+-      pseudo_random += rd.version;
++      pseudo_random += je32_to_cpu(rd->version);
+-      fd = jffs2_alloc_full_dirent(rd.nsize+1);
++      fd = jffs2_alloc_full_dirent(rd->nsize+1);
+       if (!fd) {
+               return -ENOMEM;
+-}
+-      ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
+-      if (ret) {
+-              jffs2_free_full_dirent(fd);
+-              printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", 
+-                     *ofs + sizeof(rd), ret);
+-              return ret;
+       }
+-      if (retlen != rd.nsize) {
+-              jffs2_free_full_dirent(fd);
+-              printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
+-                     retlen, *ofs + sizeof(rd), rd.nsize);
+-              return -EIO;
+-      }
+-      crc = crc32(0, fd->name, rd.nsize);
+-      if (crc != rd.name_crc) {
++      memcpy(&fd->name, rd->name, rd->nsize);
++      fd->name[rd->nsize] = 0;
++
++      crc = crc32(0, fd->name, rd->nsize);
++      if (crc != je32_to_cpu(rd->name_crc)) {
+               printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+-                     *ofs, rd.name_crc, crc); 
+-              fd->name[rd.nsize]=0;
+-              D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
++                     ofs, je32_to_cpu(rd->name_crc), crc);    
++              D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
+               jffs2_free_full_dirent(fd);
+               /* FIXME: Why do we believe totlen? */
+-              DIRTY_SPACE(PAD(rd.totlen));
+-              *ofs += PAD(rd.totlen);
++              /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
++              DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+               return 0;
+       }
+       raw = jffs2_alloc_raw_node_ref();
+@@ -674,15 +790,15 @@
+               printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+               return -ENOMEM;
+       }
+-      ic = jffs2_scan_make_ino_cache(c, rd.pino);
++      ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
+       if (!ic) {
+               jffs2_free_full_dirent(fd);
+               jffs2_free_raw_node_ref(raw);
+               return -ENOMEM;
+       }
+       
+-      raw->totlen = PAD(rd.totlen);
+-      raw->flash_offset = *ofs;
++      raw->__totlen = PAD(je32_to_cpu(rd->totlen));
++      raw->flash_offset = ofs | REF_PRISTINE;
+       raw->next_phys = NULL;
+       raw->next_in_ino = ic->nodes;
+       ic->nodes = raw;
+@@ -692,24 +808,15 @@
+               jeb->last_node->next_phys = raw;
+       jeb->last_node = raw;
+-      if (rd.nodetype & JFFS2_NODE_ACCURATE) {
+               fd->raw = raw;
+               fd->next = NULL;
+-              fd->version = rd.version;
+-              fd->ino = rd.ino;
+-              fd->name[rd.nsize]=0;
+-              fd->nhash = full_name_hash(fd->name, rd.nsize);
+-              fd->type = rd.type;
+-
+-              USED_SPACE(PAD(rd.totlen));
+-              jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
+-      } else {
+-              raw->flash_offset |= 1;
+-              jffs2_free_full_dirent(fd);
++      fd->version = je32_to_cpu(rd->version);
++      fd->ino = je32_to_cpu(rd->ino);
++      fd->nhash = full_name_hash(fd->name, rd->nsize);
++      fd->type = rd->type;
++      USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
++      jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+-              DIRTY_SPACE(PAD(rd.totlen));
+-      } 
+-      *ofs += PAD(rd.totlen);
+       return 0;
+ }
+@@ -731,26 +838,90 @@
+       struct list_head *n = head->next;
+       list_del(head);
+-      while(count--)
++      while(count--) {
+               n = n->next;
++      }
+       list_add(head, n);
+ }
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c)
++void jffs2_rotate_lists(struct jffs2_sb_info *c)
+ {
+       uint32_t x;
++      uint32_t rotateby;
+       x = count_list(&c->clean_list);
+-      if (x)
+-              rotate_list((&c->clean_list), pseudo_random % x);
++      if (x) {
++              rotateby = pseudo_random % x;
++              D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
++
++              rotate_list((&c->clean_list), rotateby);
++
++              D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
++                        list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
++      }
++
++      x = count_list(&c->very_dirty_list);
++      if (x) {
++              rotateby = pseudo_random % x;
++              D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
++
++              rotate_list((&c->very_dirty_list), rotateby);
++
++              D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
++                        list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
++      }
+       x = count_list(&c->dirty_list);
+-      if (x)
+-              rotate_list((&c->dirty_list), pseudo_random % x);
++      if (x) {
++              rotateby = pseudo_random % x;
++              D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
+-      if (c->nr_erasing_blocks)
+-              rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
++              rotate_list((&c->dirty_list), rotateby);
+-      if (c->nr_free_blocks) /* Not that it should ever be zero */
+-              rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
++              D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
++                        list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
++      }
++
++      x = count_list(&c->erasable_list);
++      if (x) {
++              rotateby = pseudo_random % x;
++              D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
++
++              rotate_list((&c->erasable_list), rotateby);
++
++              D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
++                        list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
++      }
++
++      if (c->nr_erasing_blocks) {
++              rotateby = pseudo_random % c->nr_erasing_blocks;
++              D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
++
++              rotate_list((&c->erase_pending_list), rotateby);
++
++              D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
++                        list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
++      }
++
++      if (c->nr_free_blocks) {
++              rotateby = pseudo_random % c->nr_free_blocks;
++              D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
++
++              rotate_list((&c->free_list), rotateby);
++
++              D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
++                        list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
++      } else {
++              D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
++      }
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/super-v24.c
+@@ -0,0 +1,170 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/jffs2.h>
++#include <linux/pagemap.h>
++#include <linux/mtd/mtd.h>
++#include "compr.h"
++#include "nodelist.h"
++
++#ifndef MTD_BLOCK_MAJOR
++#define MTD_BLOCK_MAJOR 31
++#endif
++
++static void jffs2_put_super (struct super_block *);
++
++static struct super_operations jffs2_super_operations =
++{
++      .read_inode =   jffs2_read_inode,
++      .put_super =    jffs2_put_super,
++      .write_super =  jffs2_write_super,
++      .statfs =       jffs2_statfs,
++      .remount_fs =   jffs2_remount_fs,
++      .clear_inode =  jffs2_clear_inode,
++      .dirty_inode =  jffs2_dirty_inode,
++};
++
++
++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++{
++      struct jffs2_sb_info *c;
++      int ret;
++
++      D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++
++      if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
++              if (!silent)
++                      printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
++              return NULL;
++      }
++
++      c = JFFS2_SB_INFO(sb);
++      memset(c, 0, sizeof(*c));
++
++      sb->s_op = &jffs2_super_operations;
++
++      c->mtd = get_mtd_device(NULL, minor(sb->s_dev));
++      if (!c->mtd) {
++              D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev)));
++              return NULL;
++      }
++
++      ret = jffs2_do_fill_super(sb, data, silent);
++      if (ret) {
++              put_mtd_device(c->mtd);
++              return NULL;
++      }
++
++      return sb;
++}
++
++static void jffs2_put_super (struct super_block *sb)
++{
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++      D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
++
++
++      if (!(sb->s_flags & MS_RDONLY))
++              jffs2_stop_garbage_collect_thread(c);
++      down(&c->alloc_sem);
++      jffs2_flush_wbuf_pad(c);
++      up(&c->alloc_sem);
++      jffs2_free_ino_caches(c);
++      jffs2_free_raw_node_refs(c);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
++              kfree(c->blocks);
++      jffs2_flash_cleanup(c);
++      kfree(c->inocache_list);
++      if (c->mtd->sync)
++              c->mtd->sync(c->mtd);
++      put_mtd_device(c->mtd);
++      
++      D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
++}
++
++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++
++static int __init init_jffs2_fs(void)
++{
++      int ret;
++
++      printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++             " (NAND)"
++#endif
++             " (C) 2001-2003 Red Hat, Inc.\n");
++
++#ifdef JFFS2_OUT_OF_KERNEL
++      /* sanity checks. Could we do these at compile time? */
++      if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
++              printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", 
++                     sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
++              return -EIO;
++      }
++
++      if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
++              printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", 
++                     sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
++              return -EIO;
++      }
++#endif
++      ret = jffs2_compressors_init();
++      if (ret) {
++              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
++              goto out;
++      }
++      ret = jffs2_create_slab_caches();
++      if (ret) {
++              printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
++              goto out_compressors;
++      }
++      ret = register_filesystem(&jffs2_fs_type);
++      if (ret) {
++              printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
++              goto out_slab;
++      }
++      return 0;
++
++ out_slab:
++      jffs2_destroy_slab_caches();
++ out_compressors:
++      jffs2_compressors_exit();
++ out:
++      return ret;
++}
++
++static void __exit exit_jffs2_fs(void)
++{
++      jffs2_destroy_slab_caches();
++      jffs2_compressors_exit();
++      unregister_filesystem(&jffs2_fs_type);
++}
++
++module_init(init_jffs2_fs);
++module_exit(exit_jffs2_fs);
++
++MODULE_DESCRIPTION("The Journalling Flash File System, v2");
++MODULE_AUTHOR("Red Hat, Inc.");
++MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for 
++                     // the sake of this tag. It's Free Software.
+--- linux-2.4.21/fs/jffs2/super.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/super.c
+@@ -1,291 +1,270 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/fs.h>
++#include <linux/mount.h>
+ #include <linux/jffs2.h>
+ #include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/namei.h>
++#include "compr.h"
+ #include "nodelist.h"
+-#ifndef MTD_BLOCK_MAJOR
+-#define MTD_BLOCK_MAJOR 31
+-#endif
++static void jffs2_put_super(struct super_block *);
+-extern void jffs2_read_inode (struct inode *);
+-void jffs2_put_super (struct super_block *);
+-void jffs2_write_super (struct super_block *);
+-static int jffs2_statfs (struct super_block *, struct statfs *);
+-int jffs2_remount_fs (struct super_block *, int *, char *);
+-extern void jffs2_clear_inode (struct inode *);
++static kmem_cache_t *jffs2_inode_cachep;
++
++static struct inode *jffs2_alloc_inode(struct super_block *sb)
++{
++      struct jffs2_inode_info *ei;
++      ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
++      if (!ei)
++              return NULL;
++      return &ei->vfs_inode;
++}
++
++static void jffs2_destroy_inode(struct inode *inode)
++{
++      kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
++}
++
++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
++{
++      struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
++
++      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++          SLAB_CTOR_CONSTRUCTOR) {
++              init_MUTEX_LOCKED(&ei->sem);
++              inode_init_once(&ei->vfs_inode);
++      }
++}
++
++static int jffs2_sync_fs(struct super_block *sb, int wait)
++{
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++      down(&c->alloc_sem);
++      jffs2_flush_wbuf_pad(c);
++      up(&c->alloc_sem);      
++      return 0;
++}
+ static struct super_operations jffs2_super_operations =
+ {
+-      read_inode:     jffs2_read_inode,
+-//    delete_inode:   jffs2_delete_inode,
+-      put_super:      jffs2_put_super,
+-      write_super:    jffs2_write_super,
+-      statfs:         jffs2_statfs,
+-      remount_fs:     jffs2_remount_fs,
+-      clear_inode:    jffs2_clear_inode
++      .alloc_inode =  jffs2_alloc_inode,
++      .destroy_inode =jffs2_destroy_inode,
++      .read_inode =   jffs2_read_inode,
++      .put_super =    jffs2_put_super,
++      .write_super =  jffs2_write_super,
++      .statfs =       jffs2_statfs,
++      .remount_fs =   jffs2_remount_fs,
++      .clear_inode =  jffs2_clear_inode,
++      .dirty_inode =  jffs2_dirty_inode,
++      .sync_fs =      jffs2_sync_fs,
+ };
+-static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
++static int jffs2_sb_compare(struct super_block *sb, void *data)
+ {
++      struct jffs2_sb_info *p = data;
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+-      unsigned long avail;
+-      buf->f_type = JFFS2_SUPER_MAGIC;
+-      buf->f_bsize = 1 << PAGE_SHIFT;
+-      buf->f_blocks = c->flash_size >> PAGE_SHIFT;
+-      buf->f_files = 0;
+-      buf->f_ffree = 0;
+-      buf->f_namelen = JFFS2_MAX_NAME_LEN;
++      /* The superblocks are considered to be equivalent if the underlying MTD
++         device is the same one */
++      if (c->mtd == p->mtd) {
++              D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
++              return 1;
++      } else {
++              D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
++                        c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
++              return 0;
++      }
++}
+-      spin_lock_bh(&c->erase_completion_lock);
++static int jffs2_sb_set(struct super_block *sb, void *data)
++{
++      struct jffs2_sb_info *p = data;
+-      avail = c->dirty_size + c->free_size;
+-      if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
+-              avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
+-      else
+-              avail = 0;
++      /* For persistence of NFS exports etc. we use the same s_dev
++         each time we mount the device, don't just use an anonymous
++         device */
++      sb->s_fs_info = p;
++      p->os_priv = sb;
++      sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
+-      buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++      return 0;
++}
+-#if CONFIG_JFFS2_FS_DEBUG > 0
+-      printk(KERN_DEBUG "STATFS:\n");
+-      printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+-      printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+-      printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+-      printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+-      printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+-      printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+-      printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
++static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
++                                            int flags, const char *dev_name, 
++                                            void *data, struct mtd_info *mtd)
++{
++      struct super_block *sb;
++      struct jffs2_sb_info *c;
++      int ret;
+-      if (c->nextblock) {
+-              printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
+-      } else {
+-              printk(KERN_DEBUG "nextblock: NULL\n");
+-      }
+-      if (c->gcblock) {
+-              printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
+-      } else {
+-              printk(KERN_DEBUG "gcblock: NULL\n");
+-      }
+-      if (list_empty(&c->clean_list)) {
+-              printk(KERN_DEBUG "clean_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      c = kmalloc(sizeof(*c), GFP_KERNEL);
++      if (!c)
++              return ERR_PTR(-ENOMEM);
++      memset(c, 0, sizeof(*c));
++      c->mtd = mtd;
+-              list_for_each(this, &c->clean_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->dirty_list)) {
+-              printk(KERN_DEBUG "dirty_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
+-              list_for_each(this, &c->dirty_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->erasing_list)) {
+-              printk(KERN_DEBUG "erasing_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      if (IS_ERR(sb))
++              goto out_put;
+-              list_for_each(this, &c->erasing_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
+-              }
++      if (sb->s_root) {
++              /* New mountpoint for JFFS2 which is already mounted */
++              D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
++                        mtd->index, mtd->name));
++              goto out_put;
+       }
+-      if (list_empty(&c->erase_pending_list)) {
+-              printk(KERN_DEBUG "erase_pending_list: empty\n");
+-      } else {
+-              struct list_head *this;
+-              list_for_each(this, &c->erase_pending_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->free_list)) {
+-              printk(KERN_DEBUG "free_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
++                mtd->index, mtd->name));
+-              list_for_each(this, &c->free_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->bad_list)) {
+-              printk(KERN_DEBUG "bad_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      sb->s_op = &jffs2_super_operations;
++      sb->s_flags = flags | MS_NOATIME;
+-              list_for_each(this, &c->bad_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->bad_used_list)) {
+-              printk(KERN_DEBUG "bad_used_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
+-              list_for_each(this, &c->bad_used_list) {
+-                      struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+-                      printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
+-              }
++      if (ret) {
++              /* Failure case... */
++              up_write(&sb->s_umount);
++              deactivate_super(sb);
++              return ERR_PTR(ret);
+       }
+-#endif /* CONFIG_JFFS2_FS_DEBUG */
+-      spin_unlock_bh(&c->erase_completion_lock);
++      sb->s_flags |= MS_ACTIVE;
++      return sb;
++ out_put:
++      kfree(c);
++      put_mtd_device(mtd);
+-      return 0;
++      return sb;
+ }
+-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
++                                            int flags, const char *dev_name, 
++                                            void *data, int mtdnr)
+ {
+-      struct jffs2_sb_info *c;
+-      struct inode *root_i;
+-      int i;
+-
+-      D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++      struct mtd_info *mtd;
+-      if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
+-              if (!silent)
+-                      printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
+-              return NULL;
++      mtd = get_mtd_device(NULL, mtdnr);
++      if (!mtd) {
++              D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
++              return ERR_PTR(-EINVAL);
+       }
+-      c = JFFS2_SB_INFO(sb);
+-      memset(c, 0, sizeof(*c));
++      return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++}
+       
+-      c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+-      if (!c->mtd) {
+-              D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+-              return NULL;
++static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
++                                      int flags, const char *dev_name,
++                                      void *data)
++{
++      int err;
++      struct nameidata nd;
++      int mtdnr;
++
++      if (!dev_name)
++              return ERR_PTR(-EINVAL);
++
++      D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
++
++      /* The preferred way of mounting in future; especially when
++         CONFIG_BLK_DEV is implemented - we specify the underlying
++         MTD device by number or by name, so that we don't require 
++         block device support to be present in the kernel. */
++
++      /* FIXME: How to do the root fs this way? */
++
++      if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
++              /* Probably mounting without the blkdev crap */
++              if (dev_name[3] == ':') {
++                      struct mtd_info *mtd;
++
++                      /* Mount by MTD device name */
++                      D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
++                      for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
++                              mtd = get_mtd_device(NULL, mtdnr);
++                              if (mtd) {
++                                      if (!strcmp(mtd->name, dev_name+4))
++                                              return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++                                      put_mtd_device(mtd);
+       }
+-      c->sector_size = c->mtd->erasesize;
+-      c->free_size = c->flash_size = c->mtd->size;
+-      c->nr_blocks = c->mtd->size / c->mtd->erasesize;
+-      c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+-      if (!c->blocks)
+-              goto out_mtd;
+-      for (i=0; i<c->nr_blocks; i++) {
+-              INIT_LIST_HEAD(&c->blocks[i].list);
+-              c->blocks[i].offset = i * c->sector_size;
+-              c->blocks[i].free_size = c->sector_size;
+-              c->blocks[i].dirty_size = 0;
+-              c->blocks[i].used_size = 0;
+-              c->blocks[i].first_node = NULL;
+-              c->blocks[i].last_node = NULL;
+       }
++                      printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
++              } else if (isdigit(dev_name[3])) {
++                      /* Mount by MTD device number name */
++                      char *endptr;
+               
+-      spin_lock_init(&c->nodelist_lock);
+-      init_MUTEX(&c->alloc_sem);
+-      init_waitqueue_head(&c->erase_wait);
+-      spin_lock_init(&c->erase_completion_lock);
+-      spin_lock_init(&c->inocache_lock);
++                      mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
++                      if (!*endptr) {
++                              /* It was a valid number */
++                              D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
++                              return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++                      }
++              }
++      }
+-      INIT_LIST_HEAD(&c->clean_list);
+-      INIT_LIST_HEAD(&c->dirty_list);
+-      INIT_LIST_HEAD(&c->erasing_list);
+-      INIT_LIST_HEAD(&c->erase_pending_list);
+-      INIT_LIST_HEAD(&c->erase_complete_list);
+-      INIT_LIST_HEAD(&c->free_list);
+-      INIT_LIST_HEAD(&c->bad_list);
+-      INIT_LIST_HEAD(&c->bad_used_list);
+-      c->highest_ino = 1;
++      /* Try the old way - the hack where we allowed users to mount 
++         /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
+-      if (jffs2_build_filesystem(c)) {
+-              D1(printk(KERN_DEBUG "build_fs failed\n"));
+-              goto out_nodes;
+-      }
++      err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+-      sb->s_op = &jffs2_super_operations;
++      D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
++                err, nd.dentry->d_inode));
+-      D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
+-      root_i = iget(sb, 1);
+-      if (is_bad_inode(root_i)) {
+-              D1(printk(KERN_WARNING "get root inode failed\n"));
+-              goto out_nodes;
++      if (err)
++              return ERR_PTR(err);
++
++      err = -EINVAL;
++
++      if (!S_ISBLK(nd.dentry->d_inode->i_mode))
++              goto out;
++
++      if (nd.mnt->mnt_flags & MNT_NODEV) {
++              err = -EACCES;
++              goto out;
+       }
+-      D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
+-      sb->s_root = d_alloc_root(root_i);
+-      if (!sb->s_root)
+-              goto out_root_i;
++      if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
++              if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
++                      printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
++                             dev_name);
++              goto out;
++      }
+-#if LINUX_VERSION_CODE >= 0x20403
+-      sb->s_maxbytes = 0xFFFFFFFF;
+-#endif
+-      sb->s_blocksize = PAGE_CACHE_SIZE;
+-      sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+-      sb->s_magic = JFFS2_SUPER_MAGIC;
+-      if (!(sb->s_flags & MS_RDONLY))
+-              jffs2_start_garbage_collect_thread(c);
+-      return sb;
++      mtdnr = iminor(nd.dentry->d_inode);
++      path_release(&nd);
+- out_root_i:
+-      iput(root_i);
+- out_nodes:
+-      jffs2_free_ino_caches(c);
+-      jffs2_free_raw_node_refs(c);
+-      kfree(c->blocks);
+- out_mtd:
+-      put_mtd_device(c->mtd);
+-      return NULL;
++      return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++
++out:
++      path_release(&nd);
++      return ERR_PTR(err);
+ }
+-void jffs2_put_super (struct super_block *sb)
++static void jffs2_put_super (struct super_block *sb)
+ {
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+@@ -293,83 +272,65 @@
+       if (!(sb->s_flags & MS_RDONLY))
+               jffs2_stop_garbage_collect_thread(c);
++      down(&c->alloc_sem);
++      jffs2_flush_wbuf_pad(c);
++      up(&c->alloc_sem);
+       jffs2_free_ino_caches(c);
+       jffs2_free_raw_node_refs(c);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
+       kfree(c->blocks);
++      jffs2_flash_cleanup(c);
++      kfree(c->inocache_list);
+       if (c->mtd->sync)
+               c->mtd->sync(c->mtd);
+-      put_mtd_device(c->mtd);
+       
+       D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+ }
+-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
+-{
+-      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+-
+-      if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
+-              return -EROFS;
+-
+-      /* We stop if it was running, then restart if it needs to.
+-         This also catches the case where it was stopped and this
+-         is just a remount to restart it */
+-      if (!(sb->s_flags & MS_RDONLY))
+-              jffs2_stop_garbage_collect_thread(c);
+-
+-      if (!(*flags & MS_RDONLY))
+-              jffs2_start_garbage_collect_thread(c);
+-      
+-      sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+-
+-      return 0;
+-}
+-
+-void jffs2_write_super (struct super_block *sb)
++static void jffs2_kill_sb(struct super_block *sb)
+ {
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+-      sb->s_dirt = 0;
+-
+-      if (sb->s_flags & MS_RDONLY)
+-              return;
+-
+-      jffs2_garbage_collect_trigger(c);
+-      jffs2_erase_pending_blocks(c);
+-      jffs2_mark_erased_blocks(c);
++      generic_shutdown_super(sb);
++      put_mtd_device(c->mtd);
++      kfree(c);
+ }
+-
+-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++static struct file_system_type jffs2_fs_type = {
++      .owner =        THIS_MODULE,
++      .name =         "jffs2",
++      .get_sb =       jffs2_get_sb,
++      .kill_sb =      jffs2_kill_sb,
++};
+ static int __init init_jffs2_fs(void)
+ {
+       int ret;
+-      printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+-      /* sanity checks. Could we do these at compile time? */
+-      if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
+-              printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", 
+-                     sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
+-              return -EIO;
+-      }
+-
+-      if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
+-              printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", 
+-                     sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
+-              return -EIO;
+-      }
++      printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++             " (NAND)"
+ #endif
++             " (C) 2001-2003 Red Hat, Inc.\n");
+-      ret = jffs2_zlib_init();
++      jffs2_inode_cachep = kmem_cache_create("jffs2_i",
++                                           sizeof(struct jffs2_inode_info),
++                                           0, SLAB_RECLAIM_ACCOUNT,
++                                           jffs2_i_init_once, NULL);
++      if (!jffs2_inode_cachep) {
++              printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
++              return -ENOMEM;
++      }
++      ret = jffs2_compressors_init();
+       if (ret) {
+-              printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
++              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
+               goto out;
+       }
+       ret = jffs2_create_slab_caches();
+       if (ret) {
+               printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+-              goto out_zlib;
++              goto out_compressors;
+       }
+       ret = register_filesystem(&jffs2_fs_type);
+       if (ret) {
+@@ -380,17 +341,19 @@
+  out_slab:
+       jffs2_destroy_slab_caches();
+- out_zlib:
+-      jffs2_zlib_exit();
++ out_compressors:
++      jffs2_compressors_exit();
+  out:
++      kmem_cache_destroy(jffs2_inode_cachep);
+       return ret;
+ }
+ static void __exit exit_jffs2_fs(void)
+ {
+-      jffs2_destroy_slab_caches();
+-      jffs2_zlib_exit();
+       unregister_filesystem(&jffs2_fs_type);
++      jffs2_destroy_slab_caches();
++      jffs2_compressors_exit();
++      kmem_cache_destroy(jffs2_inode_cachep);
+ }
+ module_init(init_jffs2_fs);
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/symlink-v24.c
+@@ -0,0 +1,52 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include "nodelist.h"
++
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
++
++struct inode_operations jffs2_symlink_inode_operations =
++{     
++      .readlink =     jffs2_readlink,
++      .follow_link =  jffs2_follow_link,
++      .setattr =      jffs2_setattr
++};
++
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
++              return -EIO;
++      }
++
++      return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
++}
++
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++              return -EIO;
++      }
++      
++      return vfs_follow_link(nd, (char *)f->dents);
++}
+--- linux-2.4.21/fs/jffs2/symlink.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/symlink.c
+@@ -3,35 +3,11 @@
+  *
+  * Copyright (C) 2001, 2002 Red Hat, Inc.
+  *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+@@ -39,72 +15,49 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
++#include <linux/namei.h>
+ #include "nodelist.h"
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+ struct inode_operations jffs2_symlink_inode_operations =
+ {     
+-      readlink:       jffs2_readlink,
+-      follow_link:    jffs2_follow_link,
+-      setattr:        jffs2_setattr
++      .readlink =     generic_readlink,
++      .follow_link =  jffs2_follow_link,
++      .setattr =      jffs2_setattr
+ };
+-static char *jffs2_getlink(struct dentry *dentry)
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+ {
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+-      char *buf;
+-      int ret;
+-      down(&f->sem);
+-      if (!f->metadata) {
+-              up(&f->sem);
+-              printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
+-              return ERR_PTR(-EINVAL);
+-      }
+-      buf = kmalloc(f->metadata->size+1, GFP_USER);
+-      if (!buf) {
+-              up(&f->sem);
+-              return ERR_PTR(-ENOMEM);
+-      }
+-      buf[f->metadata->size]=0;
++      /*
++       * We don't acquire the f->sem mutex here since the only data we
++       * use is f->dents which in case of the symlink inode points to the
++       * symlink's target path.
++       *
++       * 1. If we are here the inode has already built and f->dents has
++       * to point to the target path.
++       * 2. Nobody uses f->dents (if the inode is symlink's inode). The
++       * exception is inode freeing function which frees f->dents. But
++       * it can't be called while we are here and before VFS has
++       * stopped using our f->dents string which we provide by means of
++       * nd_set_link() call.
++       */
+-      ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
+-      up(&f->sem);
+-      if (ret) {
+-              kfree(buf);
+-              return ERR_PTR(ret);
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++              return -EIO;
+       }
+-      return buf;
+-
+-}
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+-{
+-      unsigned char *kbuf;
+-      int ret;
++      D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
+-      kbuf = jffs2_getlink(dentry);
+-      if (IS_ERR(kbuf))
+-              return PTR_ERR(kbuf);
++      nd_set_link(nd, (char *)f->dents);
+-      ret = vfs_readlink(dentry, buffer, buflen, kbuf);
+-      kfree(kbuf);
+-      return ret;
++      /*
++       * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
++       * since the only way that may cause f->dents to be changed is iput() operation.
++       * But VFS will not use f->dents after iput() has been called.
++       */
++      return 0;
+ }
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+-{
+-      unsigned char *buf;
+-      int ret;
+-
+-      buf = jffs2_getlink(dentry);
+-
+-      if (IS_ERR(buf))
+-              return PTR_ERR(buf);
+-
+-      ret = vfs_follow_link(nd, buf);
+-      kfree(buf);
+-      return ret;
+-}
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/wbuf.c
+@@ -0,0 +1,1240 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/crc32.h>
++#include <linux/mtd/nand.h>
++#include "nodelist.h"
++
++/* For testing write failures */
++#undef BREAKME
++#undef BREAKMEHEADER
++
++#ifdef BREAKME
++static unsigned char *brokenbuf;
++#endif
++
++/* max. erase failures before we mark a block bad */
++#define MAX_ERASE_FAILURES    2
++
++/* two seconds timeout for timed wbuf-flushing */
++#define WBUF_FLUSH_TIMEOUT    2 * HZ
++
++struct jffs2_inodirty {
++      uint32_t ino;
++      struct jffs2_inodirty *next;
++};
++
++static struct jffs2_inodirty inodirty_nomem;
++
++static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
++{
++      struct jffs2_inodirty *this = c->wbuf_inodes;
++
++      /* If a malloc failed, consider _everything_ dirty */
++      if (this == &inodirty_nomem)
++              return 1;
++
++      /* If ino == 0, _any_ non-GC writes mean 'yes' */
++      if (this && !ino)
++              return 1;
++
++      /* Look to see if the inode in question is pending in the wbuf */
++      while (this) {
++              if (this->ino == ino)
++                      return 1;
++              this = this->next;
++      }
++      return 0;
++}
++
++static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
++{
++      struct jffs2_inodirty *this;
++
++      this = c->wbuf_inodes;
++
++      if (this != &inodirty_nomem) {
++              while (this) {
++                      struct jffs2_inodirty *next = this->next;
++                      kfree(this);
++                      this = next;
++              }
++      }
++      c->wbuf_inodes = NULL;
++}
++
++static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
++{
++      struct jffs2_inodirty *new;
++
++      /* Mark the superblock dirty so that kupdated will flush... */
++      OFNI_BS_2SFFJ(c)->s_dirt = 1;
++
++      if (jffs2_wbuf_pending_for_ino(c, ino))
++              return;
++
++      new = kmalloc(sizeof(*new), GFP_KERNEL);
++      if (!new) {
++              D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
++              jffs2_clear_wbuf_ino_list(c);
++              c->wbuf_inodes = &inodirty_nomem;
++              return;
++      }
++      new->ino = ino;
++      new->next = c->wbuf_inodes;
++      c->wbuf_inodes = new;
++      return;
++}
++
++static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
++{
++      struct list_head *this, *next;
++      static int n;
++
++      if (list_empty(&c->erasable_pending_wbuf_list))
++              return;
++
++      list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
++              struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++
++              D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
++              list_del(this);
++              if ((jiffies + (n++)) & 127) {
++                      /* Most of the time, we just erase it immediately. Otherwise we
++                         spend ages scanning it on mount, etc. */
++                      D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
++                      list_add_tail(&jeb->list, &c->erase_pending_list);
++                      c->nr_erasing_blocks++;
++                      jffs2_erase_pending_trigger(c);
++              } else {
++                      /* Sometimes, however, we leave it elsewhere so it doesn't get
++                         immediately reused, and we spread the load a bit. */
++                      D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++                      list_add_tail(&jeb->list, &c->erasable_list);
++              }
++      }
++}
++
++#define REFILE_NOTEMPTY 0
++#define REFILE_ANYWAY   1
++
++static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
++{
++      D1(printk("About to refile bad block at %08x\n", jeb->offset));
++
++      D2(jffs2_dump_block_lists(c));
++      /* File the existing block on the bad_used_list.... */
++      if (c->nextblock == jeb)
++              c->nextblock = NULL;
++      else /* Not sure this should ever happen... need more coffee */
++              list_del(&jeb->list);
++      if (jeb->first_node) {
++              D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
++              list_add(&jeb->list, &c->bad_used_list);
++      } else {
++              BUG_ON(allow_empty == REFILE_NOTEMPTY);
++              /* It has to have had some nodes or we couldn't be here */
++              D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
++              list_add(&jeb->list, &c->erase_pending_list);
++              c->nr_erasing_blocks++;
++              jffs2_erase_pending_trigger(c);
++      }
++      D2(jffs2_dump_block_lists(c));
++
++      /* Adjust its size counts accordingly */
++      c->wasted_size += jeb->free_size;
++      c->free_size -= jeb->free_size;
++      jeb->wasted_size += jeb->free_size;
++      jeb->free_size = 0;
++
++      ACCT_SANITY_CHECK(c,jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
++}
++
++/* Recover from failure to write wbuf. Recover the nodes up to the
++ * wbuf, not the one which we were starting to try to write. */
++
++static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
++{
++      struct jffs2_eraseblock *jeb, *new_jeb;
++      struct jffs2_raw_node_ref **first_raw, **raw;
++      size_t retlen;
++      int ret;
++      unsigned char *buf;
++      uint32_t start, end, ofs, len;
++
++      spin_lock(&c->erase_completion_lock);
++
++      jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
++
++      jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
++
++      /* Find the first node to be recovered, by skipping over every
++         node which ends before the wbuf starts, or which is obsolete. */
++      first_raw = &jeb->first_node;
++      while (*first_raw && 
++             (ref_obsolete(*first_raw) ||
++              (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
++              D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
++                        ref_offset(*first_raw), ref_flags(*first_raw),
++                        (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
++                        c->wbuf_ofs));
++              first_raw = &(*first_raw)->next_phys;
++      }
++
++      if (!*first_raw) {
++              /* All nodes were obsolete. Nothing to recover. */
++              D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
++              spin_unlock(&c->erase_completion_lock);
++              return;
++      }
++
++      start = ref_offset(*first_raw);
++      end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
++
++      /* Find the last node to be recovered */
++      raw = first_raw;
++      while ((*raw)) {
++              if (!ref_obsolete(*raw))
++                      end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
++
++              raw = &(*raw)->next_phys;
++      }
++      spin_unlock(&c->erase_completion_lock);
++
++      D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
++
++      buf = NULL;
++      if (start < c->wbuf_ofs) {
++              /* First affected node was already partially written.
++               * Attempt to reread the old data into our buffer. */
++
++              buf = kmalloc(end - start, GFP_KERNEL);
++              if (!buf) {
++                      printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
++
++                      goto read_failed;
++              }
++
++              /* Do the read... */
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
++              else
++                      ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
++              
++              if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) {
++                      /* ECC recovered */
++                      ret = 0;
++              }
++              if (ret || retlen != c->wbuf_ofs - start) {
++                      printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
++
++                      kfree(buf);
++                      buf = NULL;
++              read_failed:
++                      first_raw = &(*first_raw)->next_phys;
++                      /* If this was the only node to be recovered, give up */
++                      if (!(*first_raw))
++                              return;
++
++                      /* It wasn't. Go on and try to recover nodes complete in the wbuf */
++                      start = ref_offset(*first_raw);
++              } else {
++                      /* Read succeeded. Copy the remaining data from the wbuf */
++                      memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
++              }
++      }
++      /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
++         Either 'buf' contains the data, or we find it in the wbuf */
++
++
++      /* ... and get an allocation of space from a shiny new block instead */
++      ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
++      if (ret) {
++              printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
++              kfree(buf);
++              return;
++      }
++      if (end-start >= c->wbuf_pagesize) {
++              /* Need to do another write immediately, but it's possible
++                 that this is just because the wbuf itself is completely
++                 full, and there's nothing earlier read back from the 
++                 flash. Hence 'buf' isn't necessarily what we're writing 
++                 from. */
++              unsigned char *rewrite_buf = buf?:c->wbuf;
++              uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
++
++              D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
++                        towrite, ofs));
++        
++#ifdef BREAKMEHEADER
++              static int breakme;
++              if (breakme++ == 20) {
++                      printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
++                      breakme = 0;
++                      c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++                                        brokenbuf, NULL, c->oobinfo);
++                      ret = -EIO;
++              } else
++#endif
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++                                              rewrite_buf, NULL, c->oobinfo);
++              else
++                      ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
++
++              if (ret || retlen != towrite) {
++                      /* Argh. We tried. Really we did. */
++                      printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
++                      kfree(buf);
++
++                      if (retlen) {
++                              struct jffs2_raw_node_ref *raw2;
++
++                              raw2 = jffs2_alloc_raw_node_ref();
++                              if (!raw2)
++                                      return;
++
++                              raw2->flash_offset = ofs | REF_OBSOLETE;
++                              raw2->__totlen = ref_totlen(c, jeb, *first_raw);
++                              raw2->next_phys = NULL;
++                              raw2->next_in_ino = NULL;
++
++                              jffs2_add_physical_node_ref(c, raw2);
++                      }
++                      return;
++              }
++              printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
++
++              c->wbuf_len = (end - start) - towrite;
++              c->wbuf_ofs = ofs + towrite;
++              memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
++              /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
++              if (buf)
++                      kfree(buf);
++      } else {
++              /* OK, now we're left with the dregs in whichever buffer we're using */
++              if (buf) {
++                      memcpy(c->wbuf, buf, end-start);
++                      kfree(buf);
++              } else {
++                      memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
++              }
++              c->wbuf_ofs = ofs;
++              c->wbuf_len = end - start;
++      }
++
++      /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
++      new_jeb = &c->blocks[ofs / c->sector_size];
++
++      spin_lock(&c->erase_completion_lock);
++      if (new_jeb->first_node) {
++              /* Odd, but possible with ST flash later maybe */
++              new_jeb->last_node->next_phys = *first_raw;
++      } else {
++              new_jeb->first_node = *first_raw;
++      }
++
++      raw = first_raw;
++      while (*raw) {
++              uint32_t rawlen = ref_totlen(c, jeb, *raw);
++
++              D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
++                        rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
++
++              if (ref_obsolete(*raw)) {
++                      /* Shouldn't really happen much */
++                      new_jeb->dirty_size += rawlen;
++                      new_jeb->free_size -= rawlen;
++                      c->dirty_size += rawlen;
++              } else {
++                      new_jeb->used_size += rawlen;
++                      new_jeb->free_size -= rawlen;
++                      jeb->dirty_size += rawlen;
++                      jeb->used_size  -= rawlen;
++                      c->dirty_size += rawlen;
++              }
++              c->free_size -= rawlen;
++              (*raw)->flash_offset = ofs | ref_flags(*raw);
++              ofs += rawlen;
++              new_jeb->last_node = *raw;
++
++              raw = &(*raw)->next_phys;
++      }
++
++      /* Fix up the original jeb now it's on the bad_list */
++      *first_raw = NULL;
++      if (first_raw == &jeb->first_node) {
++              jeb->last_node = NULL;
++              D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
++              list_del(&jeb->list);
++              list_add(&jeb->list, &c->erase_pending_list);
++              c->nr_erasing_blocks++;
++              jffs2_erase_pending_trigger(c);
++      }
++      else
++              jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
++
++      ACCT_SANITY_CHECK(c,jeb);
++        D1(ACCT_PARANOIA_CHECK(jeb));
++
++      ACCT_SANITY_CHECK(c,new_jeb);
++        D1(ACCT_PARANOIA_CHECK(new_jeb));
++
++      spin_unlock(&c->erase_completion_lock);
++
++      D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++}
++
++/* Meaning of pad argument:
++   0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
++   1: Pad, do not adjust nextblock free_size
++   2: Pad, adjust nextblock free_size
++*/
++#define NOPAD         0
++#define PAD_NOACCOUNT 1
++#define PAD_ACCOUNTING        2
++
++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++{
++      int ret;
++      size_t retlen;
++
++      /* Nothing to do if not write-buffering the flash. In particular, we shouldn't
++         del_timer() the timer we never initialised. */
++      if (!jffs2_is_writebuffered(c))
++              return 0;
++
++      if (!down_trylock(&c->alloc_sem)) {
++              up(&c->alloc_sem);
++              printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
++              BUG();
++      }
++
++      if (!c->wbuf_len)       /* already checked c->wbuf above */
++              return 0;
++
++      /* claim remaining space on the page
++         this happens, if we have a change to a new block,
++         or if fsync forces us to flush the writebuffer.
++         if we have a switch to next page, we will not have
++         enough remaining space for this. 
++      */
++      if (pad && !jffs2_dataflash(c)) {
++              c->wbuf_len = PAD(c->wbuf_len);
++
++              /* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
++                 with 8 byte page size */
++              memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
++              
++              if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
++                      struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
++                      padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++                      padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
++                      padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
++                      padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
++              }
++      }
++      /* else jffs2_flash_writev has actually filled in the rest of the
++         buffer for us, and will deal with the node refs etc. later. */
++      
++#ifdef BREAKME
++      static int breakme;
++      if (breakme++ == 20) {
++              printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
++              breakme = 0;
++              c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
++                                      &retlen, brokenbuf, NULL, c->oobinfo);
++              ret = -EIO;
++      } else 
++#endif
++      
++      if (jffs2_cleanmarker_oob(c))
++              ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
++      else
++              ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
++
++      if (ret || retlen != c->wbuf_pagesize) {
++              if (ret)
++                      printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
++              else {
++                      printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
++                              retlen, c->wbuf_pagesize);
++                      ret = -EIO;
++              }
++
++              jffs2_wbuf_recover(c);
++
++              return ret;
++      }
++
++      spin_lock(&c->erase_completion_lock);
++
++      /* Adjust free size of the block if we padded. */
++      if (pad && !jffs2_dataflash(c)) {
++              struct jffs2_eraseblock *jeb;
++
++              jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
++
++              D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
++                        (jeb==c->nextblock)?"next":"", jeb->offset));
++
++              /* wbuf_pagesize - wbuf_len is the amount of space that's to be 
++                 padded. If there is less free space in the block than that,
++                 something screwed up */
++              if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
++                      printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
++                             c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
++                      printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
++                             jeb->offset, jeb->free_size);
++                      BUG();
++              }
++              jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++              c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++              jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++              c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++      }
++
++      /* Stick any now-obsoleted blocks on the erase_pending_list */
++      jffs2_refile_wbuf_blocks(c);
++      jffs2_clear_wbuf_ino_list(c);
++      spin_unlock(&c->erase_completion_lock);
++
++      memset(c->wbuf,0xff,c->wbuf_pagesize);
++      /* adjust write buffer offset, else we get a non contiguous write bug */
++      c->wbuf_ofs += c->wbuf_pagesize;
++      c->wbuf_len = 0;
++      return 0;
++}
++
++/* Trigger garbage collection to flush the write-buffer. 
++   If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
++   outstanding. If ino arg non-zero, do it only if a write for the 
++   given inode is outstanding. */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
++{
++      uint32_t old_wbuf_ofs;
++      uint32_t old_wbuf_len;
++      int ret = 0;
++
++      D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
++
++      if (!c->wbuf)
++              return 0;
++
++      down(&c->alloc_sem);
++      if (!jffs2_wbuf_pending_for_ino(c, ino)) {
++              D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
++              up(&c->alloc_sem);
++              return 0;
++      }
++
++      old_wbuf_ofs = c->wbuf_ofs;
++      old_wbuf_len = c->wbuf_len;
++
++      if (c->unchecked_size) {
++              /* GC won't make any progress for a while */
++              D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
++              down_write(&c->wbuf_sem);
++              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++              /* retry flushing wbuf in case jffs2_wbuf_recover
++                 left some data in the wbuf */
++              if (ret)
++                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++              up_write(&c->wbuf_sem);
++      } else while (old_wbuf_len &&
++                    old_wbuf_ofs == c->wbuf_ofs) {
++
++              up(&c->alloc_sem);
++
++              D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
++
++              ret = jffs2_garbage_collect_pass(c);
++              if (ret) {
++                      /* GC failed. Flush it with padding instead */
++                      down(&c->alloc_sem);
++                      down_write(&c->wbuf_sem);
++                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++                      /* retry flushing wbuf in case jffs2_wbuf_recover
++                         left some data in the wbuf */
++                      if (ret)
++                              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++                      up_write(&c->wbuf_sem);
++                      break;
++              }
++              down(&c->alloc_sem);
++      }
++
++      D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
++
++      up(&c->alloc_sem);
++      return ret;
++}
++
++/* Pad write-buffer to end and write it, wasting space. */
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
++{
++      int ret;
++
++      if (!c->wbuf)
++              return 0;
++
++      down_write(&c->wbuf_sem);
++      ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++      /* retry - maybe wbuf recover left some data in wbuf. */
++      if (ret)
++              ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++      up_write(&c->wbuf_sem);
++
++      return ret;
++}
++
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
++#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
++#else
++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
++#endif
++
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
++{
++      struct kvec outvecs[3];
++      uint32_t totlen = 0;
++      uint32_t split_ofs = 0;
++      uint32_t old_totlen;
++      int ret, splitvec = -1;
++      int invec, outvec;
++      size_t wbuf_retlen;
++      unsigned char *wbuf_ptr;
++      size_t donelen = 0;
++      uint32_t outvec_to = to;
++
++      /* If not NAND flash, don't bother */
++      if (!jffs2_is_writebuffered(c))
++              return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
++      
++      down_write(&c->wbuf_sem);
++
++      /* If wbuf_ofs is not initialized, set it to target address */
++      if (c->wbuf_ofs == 0xFFFFFFFF) {
++              c->wbuf_ofs = PAGE_DIV(to);
++              c->wbuf_len = PAGE_MOD(to);                     
++              memset(c->wbuf,0xff,c->wbuf_pagesize);
++      }
++
++      /* Fixup the wbuf if we are moving to a new eraseblock.  The checks below
++         fail for ECC'd NOR because cleanmarker == 16, so a block starts at
++         xxx0010.  */
++      if (jffs2_nor_ecc(c)) {
++              if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
++                      c->wbuf_ofs = PAGE_DIV(to);
++                      c->wbuf_len = PAGE_MOD(to);
++                      memset(c->wbuf,0xff,c->wbuf_pagesize);
++              }
++      }
++      
++      /* Sanity checks on target address. 
++         It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), 
++         and it's permitted to write at the beginning of a new 
++         erase block. Anything else, and you die.
++         New block starts at xxx000c (0-b = block header)
++      */
++      if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
++              /* It's a write to a new block */
++              if (c->wbuf_len) {
++                      D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
++                      ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++                      if (ret) {
++                              /* the underlying layer has to check wbuf_len to do the cleanup */
++                              D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++                              *retlen = 0;
++                              goto exit;
++                      }
++              }
++              /* set pointer to new block */
++              c->wbuf_ofs = PAGE_DIV(to);
++              c->wbuf_len = PAGE_MOD(to);                     
++      } 
++
++      if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
++              /* We're not writing immediately after the writebuffer. Bad. */
++              printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
++              if (c->wbuf_len)
++                      printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
++                                        c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
++              BUG();
++      }
++
++      /* Note outvecs[3] above. We know count is never greater than 2 */
++      if (count > 2) {
++              printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
++              BUG();
++      }
++
++      invec = 0;
++      outvec = 0;
++
++      /* Fill writebuffer first, if already in use */ 
++      if (c->wbuf_len) {
++              uint32_t invec_ofs = 0;
++
++              /* adjust alignment offset */ 
++              if (c->wbuf_len != PAGE_MOD(to)) {
++                      c->wbuf_len = PAGE_MOD(to);
++                      /* take care of alignment to next page */
++                      if (!c->wbuf_len)
++                              c->wbuf_len = c->wbuf_pagesize;
++              }
++              
++              while(c->wbuf_len < c->wbuf_pagesize) {
++                      uint32_t thislen;
++                      
++                      if (invec == count)
++                              goto alldone;
++
++                      thislen = c->wbuf_pagesize - c->wbuf_len;
++
++                      if (thislen >= invecs[invec].iov_len)
++                              thislen = invecs[invec].iov_len;
++      
++                      invec_ofs = thislen;
++
++                      memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
++                      c->wbuf_len += thislen;
++                      donelen += thislen;
++                      /* Get next invec, if actual did not fill the buffer */
++                      if (c->wbuf_len < c->wbuf_pagesize) 
++                              invec++;
++              }                       
++              
++              /* write buffer is full, flush buffer */
++              ret = __jffs2_flush_wbuf(c, NOPAD);
++              if (ret) {
++                      /* the underlying layer has to check wbuf_len to do the cleanup */
++                      D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++                      /* Retlen zero to make sure our caller doesn't mark the space dirty.
++                         We've already done everything that's necessary */
++                      *retlen = 0;
++                      goto exit;
++              }
++              outvec_to += donelen;
++              c->wbuf_ofs = outvec_to;
++
++              /* All invecs done ? */
++              if (invec == count)
++                      goto alldone;
++
++              /* Set up the first outvec, containing the remainder of the
++                 invec we partially used */
++              if (invecs[invec].iov_len > invec_ofs) {
++                      outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
++                      totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
++                      if (totlen > c->wbuf_pagesize) {
++                              splitvec = outvec;
++                              split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
++                      }
++                      outvec++;
++              }
++              invec++;
++      }
++
++      /* OK, now we've flushed the wbuf and the start of the bits
++         we have been asked to write, now to write the rest.... */
++
++      /* totlen holds the amount of data still to be written */
++      old_totlen = totlen;
++      for ( ; invec < count; invec++,outvec++ ) {
++              outvecs[outvec].iov_base = invecs[invec].iov_base;
++              totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
++              if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
++                      splitvec = outvec;
++                      split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
++                      old_totlen = totlen;
++              }
++      }
++
++      /* Now the outvecs array holds all the remaining data to write */
++      /* Up to splitvec,split_ofs is to be written immediately. The rest
++         goes into the (now-empty) wbuf */
++
++      if (splitvec != -1) {
++              uint32_t remainder;
++
++              remainder = outvecs[splitvec].iov_len - split_ofs;
++              outvecs[splitvec].iov_len = split_ofs;
++
++              /* We did cross a page boundary, so we write some now */
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
++              else
++                      ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
++              
++              if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
++                      /* At this point we have no problem,
++                         c->wbuf is empty. However refile nextblock to avoid
++                         writing again to same address.
++                      */
++                      struct jffs2_eraseblock *jeb;
++
++                      spin_lock(&c->erase_completion_lock);
++
++                      jeb = &c->blocks[outvec_to / c->sector_size];
++                      jffs2_block_refile(c, jeb, REFILE_ANYWAY);
++
++                      *retlen = 0;
++                      spin_unlock(&c->erase_completion_lock);
++                      goto exit;
++              }
++              
++              donelen += wbuf_retlen;
++              c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
++
++              if (remainder) {
++                      outvecs[splitvec].iov_base += split_ofs;
++                      outvecs[splitvec].iov_len = remainder;
++              } else {
++                      splitvec++;
++              }
++
++      } else {
++              splitvec = 0;
++      }
++
++      /* Now splitvec points to the start of the bits we have to copy
++         into the wbuf */
++      wbuf_ptr = c->wbuf;
++
++      for ( ; splitvec < outvec; splitvec++) {
++              /* Don't copy the wbuf into itself */
++              if (outvecs[splitvec].iov_base == c->wbuf)
++                      continue;
++              memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
++              wbuf_ptr += outvecs[splitvec].iov_len;
++              donelen += outvecs[splitvec].iov_len;
++      }
++      c->wbuf_len = wbuf_ptr - c->wbuf;
++
++      /* If there's a remainder in the wbuf and it's a non-GC write,
++         remember that the wbuf affects this ino */
++alldone:
++      *retlen = donelen;
++
++      if (c->wbuf_len && ino)
++              jffs2_wbuf_dirties_inode(c, ino);
++
++      ret = 0;
++      
++exit:
++      up_write(&c->wbuf_sem);
++      return ret;
++}
++
++/*
++ *    This is the entry for flash write.
++ *    Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
++*/
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
++{
++      struct kvec vecs[1];
++
++      if (!jffs2_is_writebuffered(c))
++              return c->mtd->write(c->mtd, ofs, len, retlen, buf);
++
++      vecs[0].iov_base = (unsigned char *) buf;
++      vecs[0].iov_len = len;
++      return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
++}
++
++/*
++      Handle readback from writebuffer and ECC failure return
++*/
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
++{
++      loff_t  orbf = 0, owbf = 0, lwbf = 0;
++      int     ret;
++
++      if (!jffs2_is_writebuffered(c))
++              return c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++      /* Read flash */
++      if (jffs2_cleanmarker_oob(c))
++              ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
++      else
++              ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++      if ( (ret == -EBADMSG) && (*retlen == len) ) {
++              printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
++                     len, ofs);
++              /* 
++               * We have the raw data without ECC correction in the buffer, maybe 
++               * we are lucky and all data or parts are correct. We check the node.
++               * If data are corrupted node check will sort it out.
++               * We keep this block, it will fail on write or erase and the we
++               * mark it bad. Or should we do that now? But we should give him a chance.
++               * Maybe we had a system crash or power loss before the ecc write or  
++               * a erase was completed.
++               * So we return success. :)
++               */
++              ret = 0;
++      }       
++
++      /* if no writebuffer available or write buffer empty, return */
++      if (!c->wbuf_pagesize || !c->wbuf_len)
++              return ret;;
++
++      /* if we read in a different block, return */
++      if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
++              return ret;
++
++      /* Lock only if we have reason to believe wbuf contains relevant data,
++         so that checking an erased block during wbuf recovery space allocation
++         does not deadlock. */
++      down_read(&c->wbuf_sem);
++
++      if (ofs >= c->wbuf_ofs) {
++              owbf = (ofs - c->wbuf_ofs);     /* offset in write buffer */
++              if (owbf > c->wbuf_len)         /* is read beyond write buffer ? */
++                      goto exit;
++              lwbf = c->wbuf_len - owbf;      /* number of bytes to copy */
++              if (lwbf > len) 
++                      lwbf = len;
++      } else {        
++              orbf = (c->wbuf_ofs - ofs);     /* offset in read buffer */
++              if (orbf > len)                 /* is write beyond write buffer ? */
++                      goto exit;
++              lwbf = len - orbf;              /* number of bytes to copy */
++              if (lwbf > c->wbuf_len) 
++                      lwbf = c->wbuf_len;
++      }       
++      if (lwbf > 0)
++              memcpy(buf+orbf,c->wbuf+owbf,lwbf);
++
++exit:
++      up_read(&c->wbuf_sem);
++      return ret;
++}
++
++/*
++ *    Check, if the out of band area is empty
++ */
++int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
++{
++      unsigned char *buf;
++      int     ret = 0;
++      int     i,len,page;
++      size_t  retlen;
++      int     oob_size;
++
++      /* allocate a buffer for all oob data in this sector */
++      oob_size = c->mtd->oobsize;
++      len = 4 * oob_size;
++      buf = kmalloc(len, GFP_KERNEL);
++      if (!buf) {
++              printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
++              return -ENOMEM;
++      }
++      /* 
++       * if mode = 0, we scan for a total empty oob area, else we have
++       * to take care of the cleanmarker in the first page of the block
++      */
++      ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
++      if (ret) {
++              D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++              goto out;
++      }
++      
++      if (retlen < len) {
++              D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
++                        "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
++              ret = -EIO;
++              goto out;
++      }
++      
++      /* Special check for first page */
++      for(i = 0; i < oob_size ; i++) {
++              /* Yeah, we know about the cleanmarker. */
++              if (mode && i >= c->fsdata_pos && 
++                  i < c->fsdata_pos + c->fsdata_len)
++                      continue;
++
++              if (buf[i] != 0xFF) {
++                      D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
++                                buf[page+i], page+i, jeb->offset));
++                      ret = 1; 
++                      goto out;
++              }
++      }
++
++      /* we know, we are aligned :) */        
++      for (page = oob_size; page < len; page += sizeof(long)) {
++              unsigned long dat = *(unsigned long *)(&buf[page]);
++              if(dat != -1) {
++                      ret = 1; 
++                      goto out;
++              }
++      }
++
++out:
++      kfree(buf);     
++      
++      return ret;
++}
++
++/*
++*     Scan for a valid cleanmarker and for bad blocks
++*     For virtual blocks (concatenated physical blocks) check the cleanmarker
++*     only in the first page of the first physical block, but scan for bad blocks in all
++*     physical blocks
++*/
++int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++      struct jffs2_unknown_node n;
++      unsigned char buf[2 * NAND_MAX_OOBSIZE];
++      unsigned char *p;
++      int ret, i, cnt, retval = 0;
++      size_t retlen, offset;
++      int oob_size;
++
++      offset = jeb->offset;
++      oob_size = c->mtd->oobsize;
++
++      /* Loop through the physical blocks */
++      for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
++              /* Check first if the block is bad. */
++              if (c->mtd->block_isbad (c->mtd, offset)) {
++                      D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
++                      return 2;
++              }
++              /*
++                 *    We read oob data from page 0 and 1 of the block.
++                 *    page 0 contains cleanmarker and badblock info
++                 *    page 1 contains failure count of this block
++               */
++              ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
++
++              if (ret) {
++                      D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++                      return ret;
++              }
++              if (retlen < (oob_size << 1)) {
++                      D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
++                      return -EIO;
++              }
++
++              /* Check cleanmarker only on the first physical block */
++              if (!cnt) {
++                      n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
++                      n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
++                      n.totlen = cpu_to_je32 (8);
++                      p = (unsigned char *) &n;
++
++                      for (i = 0; i < c->fsdata_len; i++) {
++                              if (buf[c->fsdata_pos + i] != p[i]) {
++                                      retval = 1;
++                              }
++                      }
++                      D1(if (retval == 1) {
++                              printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
++                              printk(KERN_WARNING "OOB at %08x was ", offset);
++                              for (i=0; i < oob_size; i++) {
++                                      printk("%02x ", buf[i]);
++                              }
++                              printk("\n");
++                      })
++              }
++              offset += c->mtd->erasesize;
++      }
++      return retval;
++}
++
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++      struct  jffs2_unknown_node n;
++      int     ret;
++      size_t  retlen;
++
++      n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++      n.totlen = cpu_to_je32(8);
++
++      ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
++      
++      if (ret) {
++              D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++              return ret;
++      }
++      if (retlen != c->fsdata_len) {
++              D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
++              return ret;
++      }
++      return 0;
++}
++
++/* 
++ * On NAND we try to mark this block bad. If the block was erased more
++ * than MAX_ERASE_FAILURES we mark it finaly bad.
++ * Don't care about failures. This block remains on the erase-pending
++ * or badblock list as long as nobody manipulates the flash with
++ * a bootloader or something like that.
++ */
++
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
++{
++      int     ret;
++
++      /* if the count is < max, we try to write the counter to the 2nd page oob area */
++      if( ++jeb->bad_count < MAX_ERASE_FAILURES)
++              return 0;
++
++      if (!c->mtd->block_markbad)
++              return 1; // What else can we do?
++
++      D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
++      ret = c->mtd->block_markbad(c->mtd, bad_offset);
++      
++      if (ret) {
++              D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++              return ret;
++      }
++      return 1;
++}
++
++#define NAND_JFFS2_OOB16_FSDALEN      8
++
++static struct nand_oobinfo jffs2_oobinfo_docecc = {
++      .useecc = MTD_NANDECC_PLACE,
++      .eccbytes = 6,
++      .eccpos = {0,1,2,3,4,5}
++};
++
++
++int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
++{
++      struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
++
++      /* Do this only, if we have an oob buffer */
++      if (!c->mtd->oobsize)
++              return 0;
++      
++      /* Cleanmarker is out-of-band, so inline size zero */
++      c->cleanmarker_size = 0;
++
++      /* Should we use autoplacement ? */
++      if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
++              D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
++              /* Get the position of the free bytes */
++              if (!oinfo->oobfree[0][1]) {
++                      printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
++                      return -ENOSPC;
++              }
++              c->fsdata_pos = oinfo->oobfree[0][0];
++              c->fsdata_len = oinfo->oobfree[0][1];
++              if (c->fsdata_len > 8)
++                      c->fsdata_len = 8;
++      } else {
++              /* This is just a legacy fallback and should go away soon */
++              switch(c->mtd->ecctype) {
++              case MTD_ECC_RS_DiskOnChip:
++                      printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
++                      c->oobinfo = &jffs2_oobinfo_docecc;
++                      c->fsdata_pos = 6;
++                      c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++                      c->badblock_pos = 15;
++                      break;
++      
++              default:
++                      D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
++                      return -EINVAL;
++              }
++      }
++      return 0;
++}
++
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++{
++      int res;
++
++      /* Initialise write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->mtd->oobblock;
++      c->wbuf_ofs = 0xFFFFFFFF;
++      
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
++
++      res = jffs2_nand_set_oobinfo(c);
++
++#ifdef BREAKME
++      if (!brokenbuf)
++              brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!brokenbuf) {
++              kfree(c->wbuf);
++              return -ENOMEM;
++      }
++      memset(brokenbuf, 0xdb, c->wbuf_pagesize);
++#endif
++      return res;
++}
++
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
++{
++      kfree(c->wbuf);
++}
++
++int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
++      c->cleanmarker_size = 0;                /* No cleanmarkers needed */
++      
++      /* Initialize write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->sector_size;
++      c->wbuf_ofs = 0xFFFFFFFF;
++
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
++
++      printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
++
++      return 0;
++}
++
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
++      kfree(c->wbuf);
++}
++
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
++      /* Cleanmarker is actually larger on the flashes */
++      c->cleanmarker_size = 16;
++
++      /* Initialize write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->mtd->eccsize;
++      c->wbuf_ofs = 0xFFFFFFFF;
++
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
++
++      return 0;
++}
++
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
++      kfree(c->wbuf);
++}
+--- linux-2.4.21/fs/jffs2/write.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/write.c
+@@ -1,154 +1,70 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
+-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
+-   fill in the raw_inode while you're at it. */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
+ {
+-      struct inode *inode;
+-      struct super_block *sb = dir_i->i_sb;
+       struct jffs2_inode_cache *ic;
+-      struct jffs2_sb_info *c;
+-      struct jffs2_inode_info *f;
+-
+-      D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+-
+-      c = JFFS2_SB_INFO(sb);
+-      memset(ri, 0, sizeof(*ri));
+       ic = jffs2_alloc_inode_cache();
+       if (!ic) {
+-              return ERR_PTR(-ENOMEM);
+-      }
+-      memset(ic, 0, sizeof(*ic));
+-      
+-      inode = new_inode(sb);
+-      
+-      if (!inode) {
+-              jffs2_free_inode_cache(ic);
+-              return ERR_PTR(-ENOMEM);
++              return -ENOMEM;
+       }
+-      /* Alloc jffs2_inode_info when that's split in 2.5 */
++      memset(ic, 0, sizeof(*ic));
+-      f = JFFS2_INODE_INFO(inode);
+-      memset(f, 0, sizeof(*f));
+-      init_MUTEX_LOCKED(&f->sem);
+       f->inocache = ic;
+-      inode->i_nlink = f->inocache->nlink = 1;
++      f->inocache->nlink = 1;
+       f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+-      f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
+-      D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
+-      jffs2_add_ino_cache(c, f->inocache);
+-
+-      ri->magic = JFFS2_MAGIC_BITMASK;
+-      ri->nodetype = JFFS2_NODETYPE_INODE;
+-      ri->totlen = PAD(sizeof(*ri));
+-      ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-      ri->mode = mode;
+-      f->highest_version = ri->version = 1;
+-      ri->uid = current->fsuid;
+-      if (dir_i->i_mode & S_ISGID) {
+-              ri->gid = dir_i->i_gid;
+-              if (S_ISDIR(mode))
+-                      ri->mode |= S_ISGID;
+-      } else {
+-              ri->gid = current->fsgid;
+-      }
+-      inode->i_mode = ri->mode;
+-      inode->i_gid = ri->gid;
+-      inode->i_uid = ri->uid;
+-      inode->i_atime = inode->i_ctime = inode->i_mtime = 
+-              ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
+-      inode->i_blksize = PAGE_SIZE;
+-      inode->i_blocks = 0;
+-      inode->i_size = 0;
+-
+-      insert_inode_hash(inode);
++      f->inocache->ino = ++c->highest_ino;
++      f->inocache->state = INO_STATE_PRESENT;
+-      return inode;
+-}
++      ri->ino = cpu_to_je32(f->inocache->ino);
+-/* This ought to be in core MTD code. All registered MTD devices
+-   without writev should have this put in place. Bug the MTD
+-   maintainer */
+-static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+-      unsigned long i;
+-      size_t totlen = 0, thislen;
+-      int ret = 0;
++      D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
++      jffs2_add_ino_cache(c, f->inocache);
+-      for (i=0; i<count; i++) {
+-              ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
+-              totlen += thislen;
+-              if (ret || thislen != vecs[i].iov_len)
+-                      break;
+-              to += vecs[i].iov_len;
+-      }
+-      if (retlen)
+-              *retlen = totlen;
+-      return ret;
+-}
++      ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++      ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
++      ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++      ri->mode = cpu_to_jemode(mode);
++      f->highest_version = 1;
++      ri->version = cpu_to_je32(f->highest_version);
+-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+-      if (mtd->writev)
+-              return mtd->writev(mtd,vecs,count,to,retlen);
+-      else
+-              return mtd_fake_writev(mtd, vecs, count, to, retlen);
++      return 0;
+ }
+-static void writecheck(struct mtd_info *mtd, __u32 ofs)
++#if CONFIG_JFFS2_FS_DEBUG > 0
++static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
+ {
+       unsigned char buf[16];
+-      ssize_t retlen;
++      size_t retlen;
+       int ret, i;
+-      ret = mtd->read(mtd, ofs, 16, &retlen, buf);
+-      if (ret && retlen != 16) {
+-              D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
++      ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
++      if (ret || (retlen != 16)) {
++              D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
+               return;
+       }
+       ret = 0;
+@@ -157,32 +73,31 @@
+                       ret = 1;
+       }
+       if (ret) {
+-              printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
++              printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
+               printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 
+                      ofs,
+                      buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+                      buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+       }
+ }
+-
+-      
++#endif
+       
+ /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, 
+    write it to the flash, link it into the existing inode/fragment list */
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs,  __u32 *writelen)
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
+ {
+-      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_raw_node_ref *raw;
+       struct jffs2_full_dnode *fn;
+-      ssize_t retlen;
+-      struct iovec vecs[2];
++      size_t retlen;
++      struct kvec vecs[2];
+       int ret;
++      int retried = 0;
++      unsigned long cnt = 2;
+-      D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
++      D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+               printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
+               BUG();
+       }
+@@ -192,10 +107,10 @@
+       vecs[1].iov_base = (unsigned char *)data;
+       vecs[1].iov_len = datalen;
+-      writecheck(c->mtd, flash_ofs);
++      D1(writecheck(c, flash_ofs));
+-      if (ri->totlen != sizeof(*ri) + datalen) {
+-              printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
++      if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
++              printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
+       }
+       raw = jffs2_alloc_raw_node_ref();
+       if (!raw)
+@@ -206,19 +121,37 @@
+               jffs2_free_raw_node_ref(raw);
+               return ERR_PTR(-ENOMEM);
+       }
+-      raw->flash_offset = flash_ofs;
+-      raw->totlen = PAD(ri->totlen);
+-      raw->next_phys = NULL;
+-      fn->ofs = ri->offset;
+-      fn->size = ri->dsize;
++      fn->ofs = je32_to_cpu(ri->offset);
++      fn->size = je32_to_cpu(ri->dsize);
+       fn->frags = 0;
++
++      /* check number of valid vecs */
++      if (!datalen || !data)
++              cnt = 1;
++ retry:
+       fn->raw = raw;
+-      ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++      raw->flash_offset = flash_ofs;
++      raw->__totlen = PAD(sizeof(*ri)+datalen);
++      raw->next_phys = NULL;
++
++      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
++              BUG_ON(!retried);
++              D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
++                              "highest version %d -> updating dnode\n", 
++                              je32_to_cpu(ri->version), f->highest_version));
++              ri->version = cpu_to_je32(++f->highest_version);
++              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++      }
++
++      ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
++                               (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
++
+       if (ret || (retlen != sizeof(*ri) + datalen)) {
+-              printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
++              printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
+                      sizeof(*ri)+datalen, flash_ofs, ret, retlen);
++
+               /* Mark the space as dirtied */
+               if (retlen) {
+                       /* Doesn't belong to any inode */
+@@ -229,48 +162,98 @@
+                          seem corrupted, in which case the scan would skip over
+                          any node we write before the original intended end of 
+                          this node */
+-                      jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
++                      raw->flash_offset |= REF_OBSOLETE;
++                      jffs2_add_physical_node_ref(c, raw);
+                       jffs2_mark_node_obsolete(c, raw);
+               } else {
+                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+                       jffs2_free_raw_node_ref(raw);
+               }
++              if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
++                      /* Try to reallocate space and retry */
++                      uint32_t dummy;
++                      struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++                      retried = 1;
++
++                      D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++                      
++                      ACCT_SANITY_CHECK(c,jeb);
++                      D1(ACCT_PARANOIA_CHECK(jeb));
++
++                      if (alloc_mode == ALLOC_GC) {
++                              ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
++                      } else {
++                              /* Locking pain */
++                              up(&f->sem);
++                              jffs2_complete_reservation(c);
++                      
++                              ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
++                              down(&f->sem);
++                      }
++                      if (!ret) {
++                              D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++
++                              ACCT_SANITY_CHECK(c,jeb);
++                              D1(ACCT_PARANOIA_CHECK(jeb));
++
++                              goto retry;
++                      }
++                      D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++                      jffs2_free_raw_node_ref(raw);
++              }
+               /* Release the full_dnode which is now useless, and return */
+               jffs2_free_full_dnode(fn);
+-              if (writelen)
+-                      *writelen = retlen;
+               return ERR_PTR(ret?ret:-EIO);
+       }
+       /* Mark the space used */
+-      jffs2_add_physical_node_ref(c, raw, retlen, 0);
++      /* If node covers at least a whole page, or if it starts at the 
++         beginning of a page and runs to the end of the file, or if 
++         it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
++      */
++      if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
++          ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
++            (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) ==  je32_to_cpu(ri->isize)))) {
++              raw->flash_offset |= REF_PRISTINE;
++      } else {
++              raw->flash_offset |= REF_NORMAL;
++      }
++      jffs2_add_physical_node_ref(c, raw);
+       /* Link into per-inode list */
++      spin_lock(&c->erase_completion_lock);
+       raw->next_in_ino = f->inocache->nodes;
+       f->inocache->nodes = raw;
++      spin_unlock(&c->erase_completion_lock);
+-      D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
+-      if (writelen)
+-              *writelen = retlen;
++      D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
++                flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), 
++                je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
++                je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
++
++      if (retried) {
++              ACCT_SANITY_CHECK(c,NULL);
++      }
+-      f->inocache->nodes = raw;
+       return fn;
+ }
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs,  __u32 *writelen)
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
+ {
+-      struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_raw_node_ref *raw;
+       struct jffs2_full_dirent *fd;
+-      ssize_t retlen;
+-      struct iovec vecs[2];
++      size_t retlen;
++      struct kvec vecs[2];
++      int retried = 0;
+       int ret;
+-      D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
+-      writecheck(c->mtd, flash_ofs);
++      D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", 
++                je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
++                je32_to_cpu(rd->name_crc)));
++      D1(writecheck(c, flash_ofs));
+-      D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
++      D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+               printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
+               BUG();
+       }
+@@ -291,44 +274,457 @@
+               jffs2_free_raw_node_ref(raw);
+               return ERR_PTR(-ENOMEM);
+       }
+-      raw->flash_offset = flash_ofs;
+-      raw->totlen = PAD(rd->totlen);
+-      raw->next_in_ino = f->inocache->nodes;
+-      f->inocache->nodes = raw;
+-      raw->next_phys = NULL;
+-      fd->version = rd->version;
+-      fd->ino = rd->ino;
++      fd->version = je32_to_cpu(rd->version);
++      fd->ino = je32_to_cpu(rd->ino);
+       fd->nhash = full_name_hash(name, strlen(name));
+       fd->type = rd->type;
+       memcpy(fd->name, name, namelen);
+       fd->name[namelen]=0;
++
++ retry:
+       fd->raw = raw;
+-      ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++      raw->flash_offset = flash_ofs;
++      raw->__totlen = PAD(sizeof(*rd)+namelen);
++      raw->next_phys = NULL;
++
++      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
++              BUG_ON(!retried);
++              D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
++                                   "highest version %d -> updating dirent\n",
++                                   je32_to_cpu(rd->version), f->highest_version));
++              rd->version = cpu_to_je32(++f->highest_version);
++              fd->version = je32_to_cpu(rd->version);
++              rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      }
++
++      ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
++                               (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
+               if (ret || (retlen != sizeof(*rd) + namelen)) {
+-                      printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
++              printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
+                              sizeof(*rd)+namelen, flash_ofs, ret, retlen);
+               /* Mark the space as dirtied */
+                       if (retlen) {
+-                              jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
++                      raw->next_in_ino = NULL;
++                      raw->flash_offset |= REF_OBSOLETE;
++                      jffs2_add_physical_node_ref(c, raw);
+                               jffs2_mark_node_obsolete(c, raw);
+                       } else {
+                               printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+                               jffs2_free_raw_node_ref(raw);
+                       }
++              if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
++                      /* Try to reallocate space and retry */
++                      uint32_t dummy;
++                      struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++                      retried = 1;
++                      D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++
++                      ACCT_SANITY_CHECK(c,jeb);
++                      D1(ACCT_PARANOIA_CHECK(jeb));
++
++                      if (alloc_mode == ALLOC_GC) {
++                              ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
++                      } else {
++                              /* Locking pain */
++                              up(&f->sem);
++                              jffs2_complete_reservation(c);
++                      
++                              ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
++                              down(&f->sem);
++                      }
++
++                      if (!ret) {
++                              D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++                              ACCT_SANITY_CHECK(c,jeb);
++                              D1(ACCT_PARANOIA_CHECK(jeb));
++                              goto retry;
++                      }
++                      D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++                      jffs2_free_raw_node_ref(raw);
++              }
+               /* Release the full_dnode which is now useless, and return */
+               jffs2_free_full_dirent(fd);
+-              if (writelen)
+-                      *writelen = retlen;
+               return ERR_PTR(ret?ret:-EIO);
+       }
+       /* Mark the space used */
+-      jffs2_add_physical_node_ref(c, raw, retlen, 0);
+-      if (writelen)
+-              *writelen = retlen;
++      raw->flash_offset |= REF_PRISTINE;
++      jffs2_add_physical_node_ref(c, raw);
++      spin_lock(&c->erase_completion_lock);
++      raw->next_in_ino = f->inocache->nodes;
+       f->inocache->nodes = raw;
++      spin_unlock(&c->erase_completion_lock);
++
++      if (retried) {
++              ACCT_SANITY_CHECK(c,NULL);
++      }
++
+       return fd;
+ }
++
++/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
++   we don't have to go digging in struct inode or its equivalent. It should set:
++   mode, uid, gid, (starting)isize, atime, ctime, mtime */
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                          struct jffs2_raw_inode *ri, unsigned char *buf, 
++                          uint32_t offset, uint32_t writelen, uint32_t *retlen)
++{
++      int ret = 0;
++      uint32_t writtenlen = 0;
++
++              D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
++                f->inocache->ino, offset, writelen));
++              
++      while(writelen) {
++              struct jffs2_full_dnode *fn;
++              unsigned char *comprbuf = NULL;
++              uint16_t comprtype = JFFS2_COMPR_NONE;
++              uint32_t phys_ofs, alloclen;
++              uint32_t datalen, cdatalen;
++              int retried = 0;
++
++      retry:
++              D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
++
++              ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
++              if (ret) {
++                      D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
++                      break;
++              }
++              down(&f->sem);
++              datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
++              cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
++
++              comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
++
++              ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++              ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++              ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
++              ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++              ri->ino = cpu_to_je32(f->inocache->ino);
++              ri->version = cpu_to_je32(++f->highest_version);
++              ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
++              ri->offset = cpu_to_je32(offset);
++              ri->csize = cpu_to_je32(cdatalen);
++              ri->dsize = cpu_to_je32(datalen);
++              ri->compr = comprtype & 0xff;
++              ri->usercompr = (comprtype >> 8 ) & 0xff;
++              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++              ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++
++              fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
++
++              jffs2_free_comprbuf(comprbuf, buf);
++
++              if (IS_ERR(fn)) {
++                      ret = PTR_ERR(fn);
++                      up(&f->sem);
++                      jffs2_complete_reservation(c);
++                      if (!retried) {
++                              /* Write error to be retried */
++                              retried = 1;
++                              D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
++                              goto retry;
++                      }
++                      break;
++              }
++              ret = jffs2_add_full_dnode_to_inode(c, f, fn);
++              if (f->metadata) {
++                      jffs2_mark_node_obsolete(c, f->metadata->raw);
++                      jffs2_free_full_dnode(f->metadata);
++                      f->metadata = NULL;
++              }
++              if (ret) {
++                      /* Eep */
++                      D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
++                      jffs2_mark_node_obsolete(c, fn->raw);
++                      jffs2_free_full_dnode(fn);
++
++                      up(&f->sem);
++                      jffs2_complete_reservation(c);
++                      break;
++              }
++              up(&f->sem);
++              jffs2_complete_reservation(c);
++              if (!datalen) {
++                      printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
++                      ret = -EIO;
++                      break;
++              }
++              D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
++              writtenlen += datalen;
++              offset += datalen;
++              writelen -= datalen;
++              buf += datalen;
++      }
++      *retlen = writtenlen;
++      return ret;
++}
++
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
++{
++      struct jffs2_raw_dirent *rd;
++      struct jffs2_full_dnode *fn;
++      struct jffs2_full_dirent *fd;
++      uint32_t alloclen, phys_ofs;
++      int ret;
++
++      /* Try to reserve enough space for both node and dirent. 
++       * Just the node will do for now, though 
++       */
++      ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
++      D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
++      if (ret) {
++              up(&f->sem);
++              return ret;
++      }
++
++      ri->data_crc = cpu_to_je32(0);
++      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++
++      fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
++
++      D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
++                jemode_to_cpu(ri->mode)));
++
++      if (IS_ERR(fn)) {
++              D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
++              /* Eeek. Wave bye bye */
++              up(&f->sem);
++              jffs2_complete_reservation(c);
++              return PTR_ERR(fn);
++      }
++      /* No data here. Only a metadata node, which will be 
++         obsoleted by the first data write
++      */
++      f->metadata = fn;
++
++      up(&f->sem);
++      jffs2_complete_reservation(c);
++      ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++              
++      if (ret) {
++              /* Eep. */
++              D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
++              return ret;
++      }
++
++      rd = jffs2_alloc_raw_dirent();
++      if (!rd) {
++              /* Argh. Now we treat it like a normal delete */
++              jffs2_complete_reservation(c);
++              return -ENOMEM;
++      }
++
++      down(&dir_f->sem);
++
++      rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++      rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++      rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++      rd->pino = cpu_to_je32(dir_f->inocache->ino);
++      rd->version = cpu_to_je32(++dir_f->highest_version);
++      rd->ino = ri->ino;
++      rd->mctime = ri->ctime;
++      rd->nsize = namelen;
++      rd->type = DT_REG;
++      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++      fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++
++      jffs2_free_raw_dirent(rd);
++      
++      if (IS_ERR(fd)) {
++              /* dirent failed to write. Delete the inode normally 
++                 as if it were the final unlink() */
++              jffs2_complete_reservation(c);
++              up(&dir_f->sem);
++              return PTR_ERR(fd);
++      }
++
++      /* Link the fd into the inode's list, obsoleting an old
++         one if necessary. */
++      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++      jffs2_complete_reservation(c);
++      up(&dir_f->sem);
++
++      return 0;
++}
++
++
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
++                  const char *name, int namelen, struct jffs2_inode_info *dead_f)
++{
++      struct jffs2_raw_dirent *rd;
++      struct jffs2_full_dirent *fd;
++      uint32_t alloclen, phys_ofs;
++      int ret;
++
++      if (1 /* alternative branch needs testing */ || 
++          !jffs2_can_mark_obsolete(c)) {
++              /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
++
++              rd = jffs2_alloc_raw_dirent();
++              if (!rd)
++                      return -ENOMEM;
++
++              ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
++              if (ret) {
++                      jffs2_free_raw_dirent(rd);
++                      return ret;
++              }
++
++              down(&dir_f->sem);
++
++              /* Build a deletion node */
++              rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++              rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++              rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++              rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++              
++              rd->pino = cpu_to_je32(dir_f->inocache->ino);
++              rd->version = cpu_to_je32(++dir_f->highest_version);
++              rd->ino = cpu_to_je32(0);
++              rd->mctime = cpu_to_je32(get_seconds());
++              rd->nsize = namelen;
++              rd->type = DT_UNKNOWN;
++              rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++              rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++              fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
++              
++              jffs2_free_raw_dirent(rd);
++
++              if (IS_ERR(fd)) {
++                      jffs2_complete_reservation(c);
++                      up(&dir_f->sem);
++                      return PTR_ERR(fd);
++              }
++
++              /* File it. This will mark the old one obsolete. */
++              jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++              up(&dir_f->sem);
++      } else {
++              struct jffs2_full_dirent **prev = &dir_f->dents;
++              uint32_t nhash = full_name_hash(name, namelen);
++
++              down(&dir_f->sem);
++
++              while ((*prev) && (*prev)->nhash <= nhash) {
++                      if ((*prev)->nhash == nhash && 
++                          !memcmp((*prev)->name, name, namelen) &&
++                          !(*prev)->name[namelen]) {
++                              struct jffs2_full_dirent *this = *prev;
++
++                              D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
++                                        this->ino, ref_offset(this->raw)));
++
++                              *prev = this->next;
++                              jffs2_mark_node_obsolete(c, (this->raw));
++                              jffs2_free_full_dirent(this);
++                              break;
++                      }
++                      prev = &((*prev)->next);
++              }
++              up(&dir_f->sem);
++      }
++
++      /* dead_f is NULL if this was a rename not a real unlink */
++      /* Also catch the !f->inocache case, where there was a dirent
++         pointing to an inode which didn't exist. */
++      if (dead_f && dead_f->inocache) { 
++
++              down(&dead_f->sem);
++
++              if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
++                      while (dead_f->dents) {
++                              /* There can be only deleted ones */
++                              fd = dead_f->dents;
++                              
++                              dead_f->dents = fd->next;
++                              
++                              if (fd->ino) {
++                                      printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
++                                             dead_f->inocache->ino, fd->name, fd->ino);
++                              } else {
++                                      D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
++                                              fd->name, dead_f->inocache->ino));
++                              }
++                              jffs2_mark_node_obsolete(c, fd->raw);
++                              jffs2_free_full_dirent(fd);
++                      }
++              }
++
++              dead_f->inocache->nlink--;
++              /* NB: Caller must set inode nlink if appropriate */
++              up(&dead_f->sem);
++      }
++
++      jffs2_complete_reservation(c);
++
++      return 0;
++}
++
++
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
++{
++      struct jffs2_raw_dirent *rd;
++      struct jffs2_full_dirent *fd;
++      uint32_t alloclen, phys_ofs;
++      int ret;
++
++      rd = jffs2_alloc_raw_dirent();
++      if (!rd)
++              return -ENOMEM;
++
++      ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++      if (ret) {
++              jffs2_free_raw_dirent(rd);
++              return ret;
++      }
++      
++      down(&dir_f->sem);
++
++      /* Build a deletion node */
++      rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++      rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++      rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++      rd->pino = cpu_to_je32(dir_f->inocache->ino);
++      rd->version = cpu_to_je32(++dir_f->highest_version);
++      rd->ino = cpu_to_je32(ino);
++      rd->mctime = cpu_to_je32(get_seconds());
++      rd->nsize = namelen;
++
++      rd->type = type;
++
++      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++      rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++      fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++      
++      jffs2_free_raw_dirent(rd);
++
++      if (IS_ERR(fd)) {
++              jffs2_complete_reservation(c);
++              up(&dir_f->sem);
++              return PTR_ERR(fd);
++      }
++
++      /* File it. This will mark the old one obsolete. */
++      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++      jffs2_complete_reservation(c);
++      up(&dir_f->sem);
++
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/writev.c
+@@ -0,0 +1,50 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/mtd/mtd.h>
++#include "nodelist.h"
++
++/* This ought to be in core MTD code. All registered MTD devices
++   without writev should have this put in place. Bug the MTD
++   maintainer */
++static inline int mtd_fake_writev(struct mtd_info *mtd, const struct kvec *vecs,
++                                unsigned long count, loff_t to, size_t *retlen)
++{
++      unsigned long i;
++      size_t totlen = 0, thislen;
++      int ret = 0;
++
++      for (i=0; i<count; i++) {
++              if (!vecs[i].iov_len)
++                      continue;
++              ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
++              totlen += thislen;
++              if (ret || thislen != vecs[i].iov_len)
++                      break;
++              to += vecs[i].iov_len;
++      }
++      if (retlen)
++              *retlen = totlen;
++      return ret;
++}
++
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
++                            unsigned long count, loff_t to, size_t *retlen)
++{
++      if (c->mtd->writev)
++              return c->mtd->writev(c->mtd, vecs, count, to, retlen);
++      else
++              return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
++}
++
+--- linux-2.4.21/include/asm-arm/arch-pxa/hardware.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/hardware.h
+@@ -127,16 +127,20 @@
+  * Implementation specifics
+  */
+-//#ifdef CONFIG_ARCH_LUBBOCK
++#ifdef CONFIG_ARCH_LUBBOCK
+ #include "lubbock.h"
+-//#endif
++#endif
+-//#ifdef CONFIG_ARCH_PXA_IDP
++#ifdef CONFIG_ARCH_PXA_IDP
+ #include "idp.h"
+-//#endif
++#endif
+-//#ifdef CONFIG_ARCH_PXA_CERF
++#ifdef CONFIG_ARCH_PXA_CERF
+ #include "cerf.h"
+-//#endif
++#endif
++
++#ifdef CONFIG_ARCH_RAMSES
++#include "ramses.h"
++#endif
+ #endif  /* _ASM_ARCH_HARDWARE_H */
+--- linux-2.4.21/include/asm-arm/arch-pxa/irqs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/irqs.h
+@@ -105,14 +105,13 @@
+ #define S0_BVD1_STSCHG        SA1111_IRQ(53)
+ #define S1_BVD1_STSCHG        SA1111_IRQ(54)
+-#define SA1111_IRQ_MAX        SA1111_IRQ(54)
+ #undef NR_IRQS
+ #define NR_IRQS               (SA1111_IRQ_MAX + 1)
+ #endif        // defined(CONFIG_SA1111)
+-#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP) 
++#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
+ #if CONFIG_SA1111
+ #define LUBBOCK_IRQ(x)        (SA1111_IRQ_MAX + 1 + (x))
+ #else
+@@ -132,6 +131,3 @@
+ #define NR_IRQS               (LUBBOCK_LAST_IRQ + 1)
+ #endif        // CONFIG_ARCH_LUBBOCK
+-
+-
+-
+--- linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h
+@@ -1051,6 +1051,7 @@
+ #define PGSR1         __REG(0x40F00024)  /* Power Manager GPIO Sleep State Register for GP[63-32] */
+ #define PGSR2         __REG(0x40F00028)  /* Power Manager GPIO Sleep State Register for GP[84-64] */
+ #define RCSR          __REG(0x40F00030)  /* Reset Controller Status Register */
++#define PMFW          __REG(0x40F00034)  /* Power Manager Fast-Sleep Wakeup Configuration Register */
+ #define PSSR_RDH      (1 << 5)        /* Read Disable Hold */
+ #define PSSR_PH               (1 << 4)        /* Peripheral Control Hold */
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/arch-pxa/ramses.h
+@@ -0,0 +1,364 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/ramses.h
++ *
++ * 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 M&N Logistik-Lösungen Online GmbH
++ *
++ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
++ *             Initial code
++ *
++ * 2002-10-08: adaption from PXA IDP to Ramses
++ * 
++ */
++
++
++/*
++ * Note: this file must be safe to include in assembly files
++ */
++
++#define RAMSES_FLASH_PHYS     (PXA_CS0_PHYS)
++#define RAMSES_ALT_FLASH_PHYS (PXA_CS1_PHYS)
++#define RAMSES_MEDIAQ_PHYS    (PXA_CS3_PHYS)
++#define RAMSES_CONTROL_PHYS   (PXA_CS4_PHYS)
++#define RAMSES_IDE_PHYS               (PXA_CS5_PHYS + 0x03000000)
++#define RAMSES_ETH_PHYS               (PXA_CS5_PHYS + 0x03400000)
++#define RAMSES_COREVOLT_PHYS  (PXA_CS5_PHYS + 0x03800000)
++#define RAMSES_CPLD_PHYS      (PXA_CS5_PHYS + 0x03C00000)
++
++/*
++ * virtual memory map
++ */
++
++#define RAMSES_IDE_BASE               (0xf0000000)
++#define RAMSES_IDE_SIZE               (1*1024*1024)
++#define RAMSES_ETH_BASE               (RAMSES_IDE_BASE + RAMSES_IDE_SIZE)
++#define RAMSES_ETH_SIZE               (1*1024*1024)
++#define RAMSES_COREVOLT_BASE  (RAMSES_ETH_BASE + RAMSES_ETH_SIZE)
++#define RAMSES_COREVOLT_SIZE  (1*1024*1024)
++#define RAMSES_CPLD_BASE      (RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_SIZE)
++#define RAMSES_CPLD_SIZE      (1*1024*1024)
++#define RAMSES_CONTROL_BASE   (RAMSES_CPLD_BASE + RAMSES_CPLD_SIZE)
++#define RAMSES_CONTROL_SIZE   (1*1024*1024)
++
++#if (RAMSES_CONTROL_BASE + RAMSES_CONTROL_SIZE) > 0xfc000000
++#error Your custom IO space is getting a bit large !!
++#endif
++
++#define CPLD_P2V(x)           ((x) - RAMSES_CPLD_PHYS + RAMSES_CPLD_BASE)
++#define CPLD_V2P(x)           ((x) - RAMSES_CPLD_BASE + RAMSES_CPLD_PHYS)
++#define CTRL_P2V(x)           ((x) - RAMSES_CONTROL_PHYS + RAMSES_CONTROL_BASE)
++#define CTRL_V2P(x)           ((x) - RAMSES_CONTROL_BASE + RAMSES_CONTROL_PHYS)
++#define CORE_P2V(x)           ((x) - RAMSES_COREVOLT_PHYS + RAMSES_COREVOLT_BASE)
++#define CORE_V2P(x)           ((x) - RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_PHYS)
++
++//smc91111 driver compatibility issue
++#define ETH_BASE              RAMSES_ETH_BASE
++
++#ifndef __ASSEMBLY__
++#  define __CPLD_REG(x)               (*((volatile unsigned long *)CPLD_P2V(x)))
++#  define __CTRL_REG(x)         (*((volatile unsigned long *)CTRL_P2V(x)))
++#  define __CORE_REG(x)         (*((volatile unsigned long *)CORE_P2V(x)))
++#else
++#  define __CPLD_REG(x)               CPLD_P2V(x)
++#  define __CTRL_REG(x)               CTRL_P2V(x)
++#  define __CORE_REG(x)               CORE_P2V(x)
++#endif
++
++/* CPLD addresses */
++
++#define RAMSES_CPLD_PERIPH_PWR_               (RAMSES_CPLD_PHYS + 0x04)
++#define RAMSES_CPLD_PERIPH_PWR                __CPLD_REG(RAMSES_CPLD_PERIPH_PWR_)
++#define PER_RESET                     (1 << 4)
++#define PER_PWR_EN                    (1 << 3)
++#define USB_HOST_PWR_EN                       (1 << 2)
++#define CORE_VAR_EN                   (1 << 0)
++
++#define RAMSES_CPLD_LED_CONTROL_      (RAMSES_CPLD_PHYS + 0x08)
++#define RAMSES_CPLD_LED_CONTROL               __CPLD_REG(RAMSES_CPLD_LED_CONTROL_)
++#define CPLD_LED2                     (1 << 6)
++#define CPLD_LED1                     (1 << 5)
++#define GSM_ACTIVE                    (1 << 4)
++
++#define RAMSES_CPLD_KB_COL_HIGH_      (RAMSES_CPLD_PHYS + 0x0C)
++#define RAMSES_CPLD_KB_COL_HIGH               __CPLD_REG(RAMSES_CPLD_KB_COL_HIGH_)
++// kbch(7)..kbch(13) on bit 0..6
++
++#define RAMSES_CPLD_KB_COL_LOW_               (RAMSES_CPLD_PHYS + 0x10)
++#define RAMSES_CPLD_KB_COL_LOW                __CPLD_REG(RAMSES_CPLD_KB_COL_LOW_)
++// kbcl(0)..kbch(6) on bit 0..6
++
++#define RAMSES_CPLD_PCCARD_EN_                (RAMSES_CPLD_PHYS + 0x14)
++#define RAMSES_CPLD_PCCARD_EN         __CPLD_REG(RAMSES_CPLD_PCCARD_EN_)
++#define PCC1_RESET                    (1 << 7)
++#define PCC0_RESET                    (1 << 6)
++#define PCC1_ENABLE                   (1 << 1)
++#define PCC0_ENABLE                   (1 << 0)
++
++#define RAMSES_CPLD_PCCARD_PWR_               (RAMSES_CPLD_PHYS + 0x28)
++#define RAMSES_CPLD_PCCARD_PWR                __CPLD_REG(RAMSES_CPLD_PCCARD_PWR_)
++#define PCC1_PWR3                     (1 << 7)
++#define PCC1_PWR2                     (1 << 6)
++#define PCC1_PWR1                     (1 << 5)
++#define PCC1_PWR0                     (1 << 4)
++#define PCC0_PWR3                     (1 << 3)
++#define PCC0_PWR2                     (1 << 2)
++#define PCC0_PWR1                     (1 << 1)
++#define PCC0_PWR0                     (1 << 0)
++
++#define RAMSES_CPLD_MISC_CTRL_                (RAMSES_CPLD_PHYS + 0x2C)
++#define RAMSES_CPLD_MISC_CTRL         __CPLD_REG(RAMSES_CPLD_MISC_CTRL_)
++#define RAMSES_IRDA_MD1                       (1 << 5)
++#define RAMSES_IRDA_MD0                       (1 << 4)
++#define RAMSES_FIR                    (1 << 3)
++
++#define RAMSES_CPLD_LCD_              (RAMSES_CPLD_PHYS + 0x30)
++#define RAMSES_CPLD_LCD                       __CPLD_REG(RAMSES_CPLD_LCD_)
++#define RAMSES_LCD_PINC                       (1 << 7)
++#define RAMSES_LCD_PUP                        (1 << 6)
++#define RAMSES_LCD_PCS                        (1 << 5)
++#define RAMSES_LCD_DISPOFF            (1 << 2)
++#define RAMSES_LCD_VCC                        (1 << 0)
++
++#define RAMSES_CPLD_FLASH_WE_         (RAMSES_CPLD_PHYS + 0x34)
++#define RAMSES_CPLD_FLASH_WE          __CPLD_REG(RAMSES_CPLD_FLASH_WE_)
++#define RAMSES_FLASH_WE                       (1 << 0)
++
++/* Read-Only registers */
++
++#define RAMSES_CPLD_KB_ROW_           (RAMSES_CPLD_PHYS + 0x50)
++#define RAMSES_CPLD_KB_ROW            __CPLD_REG(RAMSES_CPLD_KB_ROW_)
++// kbr(0)..kbr(6) on bits 0..6
++
++#define RAMSES_CPLD_PCCARD0_STATUS_   (RAMSES_CPLD_PHYS + 0x54)
++#define RAMSES_CPLD_PCCARD0_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD0_STATUS_)
++#define RAMSES_CPLD_PCCARD1_STATUS_   (RAMSES_CPLD_PHYS + 0x58)
++#define RAMSES_CPLD_PCCARD1_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD1_STATUS_) 
++#define _PCC_WRPROT                   (1 << 7)
++#define _PCC_S16                      (1 << 7)
++#define _PCC_RESET                    (1 << 6)
++#define _PCC_IRQ                      (1 << 5)
++#define _PCC_INPACK                   (1 << 4)
++#define PCC_BVD2                      (1 << 3)
++#define PCC_BVD1                      (1 << 2)
++#define PCC_VS2                               (1 << 1)
++#define PCC_VS1                               (1 << 0)
++
++#define RAMSES_CPLD_MISC_STATUS_      (RAMSES_CPLD_PHYS + 0x5C)
++#define RAMSES_CPLD_MISC_STATUS               __CPLD_REG(RAMSES_CPLD_MISC_STATUS_)
++#define RAMSES_MMC_WRPROT             (1 << 7)
++#define RAMSES_USB_OVERCURR           (1 << 4)
++#define RAMSES_CHG_STS                        (1 << 2)
++#define RAMSES_WALL_IN                        (1 << 1)
++#define RAMSES_USB_D_CON              (1 << 0)
++
++#define RAMSES_CPLD_YEAR_             (RAMSES_CPLD_PHYS + 0x60)
++#define RAMSES_CPLD_YEAR              __CPLD_REG(RAMSES_CPLD_YEAR_)
++
++#define RAMSES_CPLD_MONTH_            (RAMSES_CPLD_PHYS + 0x64)
++#define RAMSES_CPLD_MONTH             __CPLD_REG(RAMSES_CPLD_MONTH_)
++
++#define RAMSES_CPLD_DAY_              (RAMSES_CPLD_PHYS + 0x68)
++#define RAMSES_CPLD_DAY                       __CPLD_REG(RAMSES_CPLD_DAY_)
++
++#define RAMSES_CPLD_REV_              (RAMSES_CPLD_PHYS + 0x6C)
++#define RAMSES_CPLD_REV                       __CPLD_REG(RAMSES_CPLD_REV_)
++
++#define RAMSES_CPLD_VSTAT_            (RAMSES_CPLD_PHYS + 0x7C)
++#define RAMSES_CPLD_VSTAT             __CPLD_REG(RAMSES_CPLD_VSTAT_)
++#define RAMSES_BWE                    (1 << 1)
++
++
++/* Flags for ramses_flags */
++
++#define RAMSES_FLAGS_LCD_FBTURN               (1<<0)
++/* MUST stay bit 0 */
++#define RAMSES_FLAGS_SCANNER_BEAM     (1<<1)
++#define RAMSES_FLAGS_KEY_SCAN         (1<<2)
++#define RAMSES_FLAGS_KEY_SUSPEND      (1<<3)
++#define RAMSES_FLAGS_KEY_OFF          (1<<4)
++
++
++/* Offset in SMC EEPROM for LCD type */
++#define RAMSES_LCD_TYPE_OFFSET                0x23
++
++
++/* The control register on the I/O board */
++
++#define RAMSES_CONTROL_                 (RAMSES_CONTROL_PHYS + 0)
++#define RAMSES_CONTROL                  __CTRL_REG(RAMSES_CONTROL_)
++// 5c00 = 0101 1100 0000 0000
++#define RAMSES_CONTROL_SCANNER_TRIG_  (1 << 15)
++#define RAMSES_CONTROL_SCANNER_WAKE_  (1 << 14)
++#define RAMSES_CONTROL_SCANNER_PWR    (1 << 13)
++#define RAMSES_CONTROL_LED_BLUE_      (1 << 12)
++
++#define RAMSES_CONTROL_LED_ORANGE_    (1 << 11)
++#define RAMSES_CONTROL_GSM_RESET      (1 << 10)
++#define RAMSES_CONTROL_GSM_BOOT               (1 << 9)
++#define RAMSES_CONTROL_GSM_PWR                (1 << 8)
++
++#define RAMSES_CONTROL_POWEROFF               (1 << 7)
++#define       RAMSES_CONTROL_USB_INTERN       (1 << 6)
++#define RAMSES_CONTROL_MMC_PWR                (1 << 5)
++#define RAMSES_CONTROL_UART_PWR               (1 << 4)
++
++#define RAMSES_CONTROL_LCD_BLIGHT     (1 << 3)
++#define RAMSES_CONTROL_USB            (1 << 2)
++
++#define RAMSES_POWER_OFF()        { ramses_control_shadow |=  RAMSES_CONTROL_POWEROFF;      RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active low
++#define RAMSES_SCANNER_TRIG_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_TRIG_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_ON()      { ramses_control_shadow &= ~RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_OFF()     { ramses_control_shadow |=  RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_ON()    { ramses_control_shadow &= ~RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_OFF()   { ramses_control_shadow |=  RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active high
++#define RAMSES_SCANNER_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_ON()     { ramses_control_shadow |=  RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_OFF()    { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_ON()      { ramses_control_shadow |=  RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_OFF()     { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_INTERN()       { ramses_control_shadow |=  RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_EXTERN()       { ramses_control_shadow &= ~RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_ON()          { ramses_control_shadow |=  RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_OFF()         { ramses_control_shadow &= ~RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_ON()    { ramses_control_shadow |=  RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_OFF()   { ramses_control_shadow &= ~RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
++
++// Corevolt settings
++#define RAMSES_COREVOLT_          (RAMSES_COREVOLT_PHYS)
++#define RAMSES_COREVOLT           __CORE_REG(RAMSES_COREVOLT_)
++
++// Battery protocol
++#define HDQ_TMP               0x02
++#define HDQ_LMD               0x05
++#define HDQ_VSB               0x0b
++#define HDQ_CACT      0x0d
++#define HDQ_SAEH      0x0f
++#define HDQ_SAEL      0x10
++#define HDQ_RCAC      0x11
++#define HDQ_DCR               0x18
++
++
++#ifndef __ASSEMBLY__
++
++/* Ramses specific functions */
++void ramses_lcd_power_on(void);
++void ramses_lcd_power_off(void);
++void ramses_lcd_backlight_on(void);
++void ramses_lcd_backlight_off(void);
++void ramses_lcd_set_intensity(int i);
++#ifdef OLDCODE
++void ramses_lcd_set_pwm1(int p);
++#endif
++void ramses_lcd_set_brightness(int b);
++void ramses_lcd_set_contrast(int c);
++int ramses_lcd_get_intensity(void);
++int ramses_lcd_get_brightness(void);
++int ramses_lcd_get_contrast(void);
++int ramses_hdq_get_reg(unsigned char reg);
++void ramses_shut_off(void);
++void ramses_set_corevolt(int volt);
++
++
++/* shadow registers for write only registers */
++extern u16 ramses_control_shadow;
++extern int ramses_corevolt_shadow;
++extern u16 ramses_lcd_type;
++extern int ramses_lcd_pwm1_shadow;
++
++
++/* flag register for various settings */
++extern unsigned int ramses_flags;
++
++/* 
++ * macros to write to write only register
++ *
++ * none of these macros are protected from 
++ * multiple drivers using them in interrupt context.
++ */
++
++#define WRITE_RAMSES_CONTROL(value, mask) \
++{\
++      ramses_control_shadow = ((value & mask) | (ramses_control_shadow & ~mask));\
++      RAMSES_CONTROL = ramses_control_shadow;\
++}
++#endif
++
++/*
++ *  USB Host
++ *
++ *  The SL811HS is selected with nCS3 and some address bits:
++ *
++ *                               12    8    4
++ *  nA14, nCS[3], address mask 1011 1111 1111 0000 = xBf00
++ */
++#define SL811HS_PHYS (PXA_CS3_PHYS+0xBFF0)
++#define SL811HS_DATA (PXA_CS3_PHYS+0xBFF4)
++
++
++
++
++
++
++
++#define PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
++
++
++
++
++
++/* A listing of interrupts used by external hardware devices */
++
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(21)
++#define TOUCH_PANEL_IRQ_EDGE          GPIO_FALLING_EDGE
++
++#define ETHERNET_IRQ                  IRQ_GPIO(4)
++#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
++
++#define CFCARD_CD_VALID                       IRQ_GPIO(8)
++#define CFCARD_CD_VALID_EDGE          GPIO_BOTH_EDGES
++
++#define CFCARD_RDYINT                 IRQ_GPIO(22)
++
++#define RAMSES_KEYBOARD_IRQ           IRQ_GPIO(3)
++#define RAMSES_KEYBOARD_IRQ_EDGE      GPIO_FALLING_EDGE
++
++#define SL811HS_IRQ                   IRQ_GPIO(32)
++#define SL811HS_IRQ_EDGE              GPIO_RISING_EDGE
++
++/*
++ * Macros for LED Driver
++ */
++
++/* leds 0 = ON */
++#define RAMSES_HB_LED (1<<5)  
++#define RAMSES_BUSY_LED       (1<<6)
++
++#define RAMSES_LEDS_MASK              (RAMSES_HB_LED | RAMSES_BUSY_LED)
++
++#define RAMSES_WRITE_LEDS(value)      (RAMSES_CPLD_LED_CONTROL = ((RAMSES_CPLD_LED_CONTROL & ~(RAMSES_LEDS_MASK)) | value))
++
++/*
++ * macros for MTD driver
++ */
++
++#define FLASH_WRITE_PROTECT_DISABLE() ((RAMSES_CPLD_FLASH_WE) &= ~(0x1))
++#define FLASH_WRITE_PROTECT_ENABLE()  ((RAMSES_CPLD_FLASH_WE) |= (0x1))
++
++
+--- linux-2.4.21/include/asm-arm/arch-pxa/time.h~pxa-timerint
++++ linux-2.4.21/include/asm-arm/arch-pxa/time.h
+@@ -33,7 +33,7 @@
+ /* IRQs are disabled before entering here from do_gettimeofday() */
+ static unsigned long pxa_gettimeoffset (void)
+ {
+-      unsigned long ticks_to_match, elapsed, usec;
++      long ticks_to_match, elapsed, usec;
+       /* Get ticks before next timer match */
+       ticks_to_match = OSMR0 - OSCR;
+@@ -41,6 +41,10 @@
+       /* We need elapsed ticks since last match */
+       elapsed = LATCH - ticks_to_match;
++      /* don't get fooled by the workaround in pxa_timer_interrupt() */
++      if (elapsed <= 0)
++              return 0;
++
+       /* Now convert them to usec */
+       usec = (unsigned long)(elapsed*tick)/LATCH;
+@@ -59,6 +63,15 @@
+        * IRQs are disabled inside the loop to ensure coherence between
+        * lost_ticks (updated in do_timer()) and the match reg value, so we
+        * can use do_gettimeofday() from interrupt handlers.
++       *
++       * HACK ALERT: it seems that the PXA timer regs aren't updated right
++       * away in all cases when a write occurs.  We therefore compare with
++       * 8 instead of 0 in the while() condition below to avoid missing a
++       * match if OSCR has already reached the next OSMR value.
++       * Experience has shown that up to 6 ticks are needed to work around
++       * this problem, but let's use 8 to be conservative.  Note that this
++       * affect things only when the timer IRQ has been delayed by nearly
++       * exactly one tick period which should be a pretty rare event. 
+        */
+       do {
+               do_leds();
+@@ -68,7 +81,7 @@
+               OSSR = OSSR_M0;  /* Clear match on timer 0 */
+               next_match = (OSMR0 += LATCH);
+               restore_flags( flags );
+-      } while( (signed long)(next_match - OSCR) <= 0 );
++      } while( (signed long)(next_match - OSCR) <= 8 );
+ }
+ extern inline void setup_timer (void)
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/bug.h
+@@ -0,0 +1 @@
++/* dummy */
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/sl811-hw.h
+@@ -0,0 +1,202 @@
++/*
++File: include/asm-arm/sl811-hw.h
++
++19.09.2003 hne@ist1.de
++Use Kernel 2.4.20 and this source from 2.4.22
++Splitt hardware depens into file sl811-x86.h and sl811-arm.h.
++Functions as inline.
++
++23.09.2003 hne
++Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h.
++GPRD as parameter.
++
++24.09.2003 hne
++Use Offset from ADDR to DATA instand of direct io.
++
++03.10.2003 hne
++Low level only for port io into hardware-include.
++*/
++
++#ifndef __LINUX_SL811_HW_H
++#define __LINUX_SL811_HW_H
++
++#ifdef CONFIG_X86
++#define MAX_CONTROLERS                1       /* Max number of sl811 controllers */
++                                      /* Always 1 for this architecture! */
++
++#define SIZEOF_IO_REGION      1       /* Size for request/release region */
++
++#define OFFSET_DATA_REG data_off      /* Offset from ADDR_IO to DATA_IO (future) */
++                                      /* Can change by arg */
++
++static int io = 0xf100000e;   /* Base addr_io */
++static int data_off = 1;      /* Offset from addr_io to addr_io */
++static int irq = 44;          /* also change gprd !!! */
++static int gprd = 23;         /* also change irq  !!! */
++
++MODULE_PARM(io,"i");
++MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e");
++MODULE_PARM(data_off,"i");
++MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)");
++MODULE_PARM(irq,"i");
++MODULE_PARM_DESC(irq,"sl811 irq 44(default)");
++MODULE_PARM(gprd,"i");
++MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)");
++#endif
++
++#ifdef CONFIG_ARCH_RAMSES
++#define SIZEOF_IO_REGION        8       /* Size for request/release region */
++static void *ramses_sl811hs;            /* dynamically assign virtual address */
++#endif
++
++
++/*
++ * Low level: Read from Data port [arm]
++ */
++static __u8 inline sl811_read_data (struct sl811_hc *hc)
++{
++      __u8 data;
++      data = readb(hc->data_io);
++      rmb();
++//printk("%s: in  %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      return data;
++}
++
++/*
++ * Low level: Write to index register [arm]
++ */
++static void inline sl811_write_index (struct sl811_hc *hc, __u8 index)
++{
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++      writeb(index, hc->addr_io);
++      wmb();
++}
++
++/*
++ * Low level: Write to Data port [arm]
++ */
++static void inline sl811_write_data (struct sl811_hc *hc, __u8 data)
++{
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      writeb(data, hc->data_io);
++      wmb();
++}
++
++/*
++ * Low level: Write to index register and data port [arm]
++ */
++static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data)
++{
++      writeb(index, hc->addr_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++      writeb(data, hc->data_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      wmb();
++}
++
++
++/*
++ * This       function is board specific.  It sets up the interrupt to
++ * be an edge trigger and trigger on the rising       edge
++ */
++static void inline sl811_init_irq(void)
++{
++#ifdef CONFIG_X86
++      GPDR &= ~(1<<gprd);
++      set_GPIO_IRQ_edge(1<<gprd, GPIO_RISING_EDGE);
++#endif
++#ifdef CONFIG_ARCH_PXA
++      int irq_gpio_pin = IRQ_TO_GPIO_2_80(SL811HS_IRQ);
++      GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
++      set_GPIO_IRQ_edge(irq_gpio_pin, SL811HS_IRQ_EDGE);
++#endif
++}
++
++/*****************************************************************
++ *
++ * Function Name: release_regions [arm]
++ *
++ * This function is board specific. It release all io address
++ * from memory (if can).
++ *
++ * Input: struct sl811_hc * *
++ *
++ * Return value  : 0 = OK
++ *
++ *****************************************************************/
++static void inline sl811_release_regions(struct sl811_hc *hc)
++{
++#ifdef CONFIG_X86
++      if (hc->addr_io)
++              release_region(hc->addr_io, SIZEOF_IO_REGION);
++      hc->addr_io = 0;
++
++      if (hc->data_io)
++              release_region(hc->data_io, SIZEOF_IO_REGION);
++      hc->data_io = 0;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++      if (ramses_sl811hs) {
++              iounmap(ramses_sl811hs);
++              release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
++      }
++      hc->addr_io = 0;
++      hc->data_io = 0;
++      RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN;
++      RAMSES_USB_BUS_OFF();
++#endif
++}
++
++/*****************************************************************
++ *
++ * Function Name: request_regions [arm]
++ *
++ * This function is board specific. It request all io address and
++ * maps into memory (if can).
++ *
++ * Input: struct sl811_hc *
++ *
++ * Return value  : 0 = OK
++ *
++ *****************************************************************/
++static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name)
++{
++#ifdef CONFIG_X86
++      if (!request_region(addr_io, SIZEOF_IO_REGION, name)) {
++              PDEBUG(3, "request address %d failed", addr_io);
++              return -EBUSY;
++      }
++      hc->addr_io =   addr_io;
++
++      if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) {
++              PDEBUG(3, "request address %d failed", data_io);
++              /* release_region(hc->addr_io, SIZEOF_IO_REGION); */
++              return -EBUSY;
++      }
++      hc->data_io =   data_io;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++      RAMSES_USB_BUS_ON();
++      RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN;
++      mdelay(300);
++
++        if (!request_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION, name)) {
++                printk(KERN_ERR "unable to reserve region\n");
++                return -EBUSY;
++        } else {
++                ramses_sl811hs = ioremap_nocache(SL811HS_PHYS, SIZEOF_IO_REGION);
++              dbg("phys %p -> virt %p\n", SL811HS_PHYS, ramses_sl811hs);
++                if (!ramses_sl811hs) {
++                        printk(KERN_ERR "unable to map region\n");
++                        release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
++                        return -EBUSY;
++                }
++        }
++        hc->addr_io = (unsigned long) ramses_sl811hs;
++        hc->data_io = (unsigned long) ramses_sl811hs+4;
++#endif
++
++      return 0;
++}
++
++#endif // __LINUX_SL811_HW_H
+--- linux-2.4.21/include/linux/apm_bios.h~pm
++++ linux-2.4.21/include/linux/apm_bios.h
+@@ -16,6 +16,8 @@
+  * General Public License for more details.
+  */
++#include <linux/pm-devices.h>
++
+ typedef unsigned short        apm_event_t;
+ typedef unsigned short        apm_eventinfo_t;
+@@ -59,6 +61,16 @@
+ };
+ /*
++ * Allow device specific code to register function which
++ * gets the battery power status (see arch/arm/mach-pxa/apm.c).
++ */
++extern 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));
++
++/*
+  * The APM function codes
+  */
+ #define       APM_FUNC_INST_CHECK     0x5300
+@@ -168,6 +180,7 @@
+ /*
+  * APM Device IDs
+  */
++#ifdef _i386_
+ #define APM_DEVICE_BIOS               0x0000
+ #define APM_DEVICE_ALL                0x0001
+ #define APM_DEVICE_DISPLAY    0x0100
+@@ -181,6 +194,21 @@
+ #define APM_DEVICE_OLD_ALL    0xffff
+ #define APM_DEVICE_CLASS      0x00ff
+ #define APM_DEVICE_MASK               0xff00
++#endif
++
++/*
++ *  APM devices IDs for non-x86
++ */
++#define APM_DEVICE_ALL                PM_SYS_DEV
++#define APM_DEVICE_DISPLAY    PM_DISPLAY_DEV
++#define APM_DEVICE_STORAGE    PM_STORAGE_DEV
++#define APM_DEVICE_PARALLEL   PM_PARALLEL_DEV
++#define APM_DEVICE_SERIAL     PM_SERIAL_DEV
++#define APM_DEVICE_NETWORK    PM_NETWORK_DEV
++#define APM_DEVICE_PCMCIA     PM_PCMCIA_DEV
++#define APM_DEVICE_BATTERY    PM_BATTERY_DEV
++#define APM_DEVICE_TPANEL     PM_TPANEL_DEV
++
+ #ifdef __KERNEL__
+ /*
+@@ -214,5 +242,6 @@
+ #define APM_IOC_STANDBY               _IO('A', 1)
+ #define APM_IOC_SUSPEND               _IO('A', 2)
++#define APM_IOC_SET_WAKEUP    _IO('A', 3)
+ #endif        /* LINUX_APM_H */
+--- linux-2.4.21/include/linux/crc32.h~mtd-cvs
++++ linux-2.4.21/include/linux/crc32.h
+@@ -46,4 +46,25 @@
+       return crc;
+ }
+-#endif /* _LINUX_CRC32_H */
++#ifndef CRC32_H
++#define CRC32_H
++
++/* $Id$ */
++
++#include <linux/types.h>
++
++extern const uint32_t crc32_table[256];
++
++/* Return a 32-bit CRC of the contents of the buffer. */
++
++static inline uint32_t 
++crc32(uint32_t val, const void *ss, int len)
++{
++      const unsigned char *s = ss;
++        while (--len >= 0)
++                val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
++        return val;
++}
++
++#endif
++#endif
+--- linux-2.4.21/include/linux/fs.h~mtd-cvs
++++ linux-2.4.21/include/linux/fs.h
+@@ -1376,6 +1376,7 @@
+ extern void iput(struct inode *);
+ extern void force_delete(struct inode *);
+ extern struct inode * igrab(struct inode *);
++extern struct inode * ilookup(struct super_block *, unsigned long);
+ extern ino_t iunique(struct super_block *, ino_t);
+ typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+--- linux-2.4.21/include/linux/i2c-id.h~i2c-ds1337
++++ linux-2.4.21/include/linux/i2c-id.h
+@@ -95,13 +95,14 @@
+ #define I2C_DRIVERID_ADV717x  48     /* ADV 7175/7176 video encoder   */
+ #define I2C_DRIVERID_ZR36067  49     /* Zoran 36067 video encoder     */
+ #define I2C_DRIVERID_ZR36120  50     /* Zoran 36120 video encoder     */
+-#define I2C_DRIVERID_24LC32A  51              /* Microchip 24LC32A 32k EEPROM */
++#define I2C_DRIVERID_24LC32A  51     /* Microchip 24LC32A 32k EEPROM  */
++#define I2C_DRIVERID_DS1337     52     /* DS1337 real time clock        */
+-#define I2C_DRIVERID_DS1307   46      /* real time clock: DS1307      */
+-#define I2C_DRIVERID_24LC64   47      /* EEprom 24LC64                */
+-#define I2C_DRIVERID_FM24CLB4 48      /* EEprom FM24CLB4              */
++//#define I2C_DRIVERID_DS1307 46      /* real time clock: DS1307      */
++//#define I2C_DRIVERID_24LC64 47      /* EEprom 24LC64                */
++//#define I2C_DRIVERID_FM24CLB4       48      /* EEprom FM24CLB4              */
+ #define I2C_DRIVERID_EXP0     0xF0    /* experimental use id's        */
+ #define I2C_DRIVERID_EXP1     0xF1
+--- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2.h
+@@ -1,50 +1,30 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence.  You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above.  If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL.  If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
++ * For licensing information, see the file 'LICENCE' in the 
++ * jffs2 directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+ #ifndef __LINUX_JFFS2_H__
+ #define __LINUX_JFFS2_H__
+-#include <asm/types.h>
++/* You must include something which defines the C99 uintXX_t types. 
++   We don't do it from here because this file is used in too many
++   different environments. */
++
+ #define JFFS2_SUPER_MAGIC 0x72b6
+ /* Values we may expect to find in the 'magic' field */
+ #define JFFS2_OLD_MAGIC_BITMASK 0x1984
+ #define JFFS2_MAGIC_BITMASK 0x1985
+-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+ #define JFFS2_EMPTY_BITMASK 0xffff
+ #define JFFS2_DIRTY_BITMASK 0x0000
+@@ -63,6 +43,8 @@
+ #define JFFS2_COMPR_COPY      0x04
+ #define JFFS2_COMPR_DYNRUBIN  0x05
+ #define JFFS2_COMPR_ZLIB      0x06
++#define JFFS2_COMPR_LZO         0x07
++#define JFFS2_COMPR_LZARI       0x08
+ /* Compatibility flags. */
+ #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+ #define JFFS2_NODE_ACCURATE 0x2000
+@@ -78,16 +60,12 @@
+ #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+ #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+ #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+ // Maybe later...
+ //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+ //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+-/* Same as the non_ECC versions, but with extra space for real 
+- * ECC instead of just the checksum. For use on NAND flash 
+- */
+-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
+-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
+ #define JFFS2_INO_FLAG_PREREAD          1     /* Do read_inode() for this one at 
+                                          mount time, don't wait for it to 
+@@ -96,31 +74,46 @@
+                                          compression type */
++/* These can go once we've made sure we've caught all uses without
++   byteswapping */
++
++typedef struct {
++      uint32_t v32;
++} __attribute__((packed))  jint32_t;
++
++typedef struct {
++      uint32_t m;
++} __attribute__((packed))  jmode_t;
++
++typedef struct {
++      uint16_t v16;
++} __attribute__((packed)) jint16_t;
++
+ struct jffs2_unknown_node
+ {
+       /* All start like this */
+-      __u16 magic;
+-      __u16 nodetype;
+-      __u32 totlen; /* So we can skip over nodes we don't grok */
+-      __u32 hdr_crc;
++      jint16_t magic;
++      jint16_t nodetype;
++      jint32_t totlen; /* So we can skip over nodes we don't grok */
++      jint32_t hdr_crc;
+ } __attribute__((packed));
+ struct jffs2_raw_dirent
+ {
+-      __u16 magic;
+-      __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
+-      __u32 totlen;
+-      __u32 hdr_crc;
+-      __u32 pino;
+-      __u32 version;
+-      __u32 ino; /* == zero for unlink */
+-      __u32 mctime;
+-      __u8 nsize;
+-      __u8 type;
+-      __u8 unused[2];
+-      __u32 node_crc;
+-      __u32 name_crc;
+-      __u8 name[0];
++      jint16_t magic;
++      jint16_t nodetype;      /* == JFFS_NODETYPE_DIRENT */
++      jint32_t totlen;
++      jint32_t hdr_crc;
++      jint32_t pino;
++      jint32_t version;
++      jint32_t ino; /* == zero for unlink */
++      jint32_t mctime;
++      uint8_t nsize;
++      uint8_t type;
++      uint8_t unused[2];
++      jint32_t node_crc;
++      jint32_t name_crc;
++      uint8_t name[0];
+ } __attribute__((packed));
+ /* The JFFS2 raw inode structure: Used for storage on physical media.  */
+@@ -131,28 +124,28 @@
+ */
+ struct jffs2_raw_inode
+ {
+-      __u16 magic;      /* A constant magic number.  */
+-      __u16 nodetype;   /* == JFFS_NODETYPE_INODE */
+-      __u32 totlen;     /* Total length of this node (inc data, etc.) */
+-      __u32 hdr_crc;
+-      __u32 ino;        /* Inode number.  */
+-      __u32 version;    /* Version number.  */
+-      __u32 mode;       /* The file's type or mode.  */
+-      __u16 uid;        /* The file's owner.  */
+-      __u16 gid;        /* The file's group.  */
+-      __u32 isize;      /* Total resultant size of this inode (used for truncations)  */
+-      __u32 atime;      /* Last access time.  */
+-      __u32 mtime;      /* Last modification time.  */
+-      __u32 ctime;      /* Change time.  */
+-      __u32 offset;     /* Where to begin to write.  */
+-      __u32 csize;      /* (Compressed) data size */
+-      __u32 dsize;      /* Size of the node's data. (after decompression) */
+-      __u8 compr;       /* Compression algorithm used */
+-      __u8 usercompr;   /* Compression algorithm requested by the user */
+-      __u16 flags;      /* See JFFS2_INO_FLAG_* */
+-      __u32 data_crc;   /* CRC for the (compressed) data.  */
+-      __u32 node_crc;   /* CRC for the raw inode (excluding data)  */
+-//    __u8 data[dsize];
++      jint16_t magic;      /* A constant magic number.  */
++      jint16_t nodetype;   /* == JFFS_NODETYPE_INODE */
++      jint32_t totlen;     /* Total length of this node (inc data, etc.) */
++      jint32_t hdr_crc;
++      jint32_t ino;        /* Inode number.  */
++      jint32_t version;    /* Version number.  */
++      jmode_t mode;       /* The file's type or mode.  */
++      jint16_t uid;        /* The file's owner.  */
++      jint16_t gid;        /* The file's group.  */
++      jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
++      jint32_t atime;      /* Last access time.  */
++      jint32_t mtime;      /* Last modification time.  */
++      jint32_t ctime;      /* Change time.  */
++      jint32_t offset;     /* Where to begin to write.  */
++      jint32_t csize;      /* (Compressed) data size */
++      jint32_t dsize;      /* Size of the node's data. (after decompression) */
++      uint8_t compr;       /* Compression algorithm used */
++      uint8_t usercompr;   /* Compression algorithm requested by the user */
++      jint16_t flags;      /* See JFFS2_INO_FLAG_* */
++      jint32_t data_crc;   /* CRC for the (compressed) data.  */
++      jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
++      uint8_t data[0];
+ } __attribute__((packed));
+ union jffs2_node_union {
+--- linux-2.4.21/include/linux/jffs2_fs_i.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_i.h
+@@ -1,22 +1,13 @@
+-/* $Id$ */
++/* $Id$ */
+ #ifndef _JFFS2_FS_I
+ #define _JFFS2_FS_I
+-/* Include the pipe_inode_info at the beginning so that we can still
+-   use the storage space in the inode when we have a pipe inode.
+-   This sucks.
+-*/
+-
+-#undef THISSUCKS /* Only for 2.2 */
+-#ifdef THISSUCKS
+-#include <linux/pipe_fs_i.h>
+-#endif
++#include <linux/version.h>
++#include <linux/rbtree.h>
++#include <asm/semaphore.h>
+ struct jffs2_inode_info {
+-#ifdef THISSUCKS
+-        struct pipe_inode_info pipecrap;
+-#endif
+       /* We need an internal semaphore similar to inode->i_sem.
+          Unfortunately, we can't used the existing one, because
+          either the GC would deadlock, or we'd have to release it
+@@ -26,10 +17,10 @@
+       struct semaphore sem;
+       /* The highest (datanode) version number used for this ino */
+-      __u32 highest_version;
++      uint32_t highest_version;
+       /* List of data fragments which make up the file */
+-      struct jffs2_node_frag *fraglist;
++      struct rb_root fragtree;
+       /* There may be one datanode which isn't referenced by any of the
+          above fragments, if it contains a metadata update but no actual
+@@ -44,19 +35,13 @@
+       /* Some stuff we just have to keep in-core at all times, for each inode. */
+       struct jffs2_inode_cache *inocache;
+-      /* Keep a pointer to the last physical node in the list. We don't 
+-         use the doubly-linked lists because we don't want to increase
+-         the memory usage that much. This is simpler */
+-      //      struct jffs2_raw_node_ref *lastnode;
+-      __u16 flags;
+-      __u8 usercompr;
+-};
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
+-#else
+-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++      uint16_t flags;
++      uint8_t usercompr;
++#if !defined (__ECOS)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++      struct inode vfs_inode;
++#endif
+ #endif
++};
+ #endif /* _JFFS2_FS_I */
+-
+--- linux-2.4.21/include/linux/jffs2_fs_sb.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_sb.h
+@@ -1,18 +1,23 @@
+-/* $Id$ */
++/* $Id$ */
+ #ifndef _JFFS2_FS_SB
+ #define _JFFS2_FS_SB
+ #include <linux/types.h>
+ #include <linux/spinlock.h>
++#include <linux/workqueue.h>
+ #include <linux/completion.h>
+ #include <asm/semaphore.h>
++#include <linux/timer.h>
++#include <linux/wait.h>
+ #include <linux/list.h>
+-
+-#define INOCACHE_HASHSIZE 1
++#include <linux/rwsem.h>
+ #define JFFS2_SB_FLAG_RO 1
+-#define JFFS2_SB_FLAG_MOUNTING 2
++#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
++#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
++
++struct jffs2_inodirty;
+ /* A struct for the overall file system control.  Pointers to
+    jffs2_sb_info structs are named `c' in the source code.  
+@@ -21,36 +26,44 @@
+ struct jffs2_sb_info {
+       struct mtd_info *mtd;
+-      __u32 highest_ino;
++      uint32_t highest_ino;
++      uint32_t checked_ino;
++
+       unsigned int flags;
+-      spinlock_t nodelist_lock;
+-      //      pid_t thread_pid;               /* GC thread's PID */
+       struct task_struct *gc_task;    /* GC task struct */
+       struct semaphore gc_thread_start; /* GC thread start mutex */
+       struct completion gc_thread_exit; /* GC thread exit completion port */
+-      //      __u32 gc_minfree_threshold;     /* GC trigger thresholds */
+-      //      __u32 gc_maxdirty_threshold;
+       struct semaphore alloc_sem;     /* Used to protect all the following 
+                                          fields, and also to protect against
+-                                         out-of-order writing of nodes.
+-                                         And GC.
+-                                      */
+-      __u32 flash_size;
+-      __u32 used_size;
+-      __u32 dirty_size;
+-      __u32 free_size;
+-      __u32 erasing_size;
+-      __u32 bad_size;
+-      __u32 sector_size;
+-      //      __u32 min_free_size;
+-      //      __u32 max_chunk_size;
++                                         out-of-order writing of nodes. And GC. */
++      uint32_t cleanmarker_size;      /* Size of an _inline_ CLEANMARKER
++                                       (i.e. zero for OOB CLEANMARKER */
+-      __u32 nr_free_blocks;
+-      __u32 nr_erasing_blocks;
++      uint32_t flash_size;
++      uint32_t used_size;
++      uint32_t dirty_size;
++      uint32_t wasted_size;
++      uint32_t free_size;
++      uint32_t erasing_size;
++      uint32_t bad_size;
++      uint32_t sector_size;
++      uint32_t unchecked_size;
+-      __u32 nr_blocks;
++      uint32_t nr_free_blocks;
++      uint32_t nr_erasing_blocks;
++
++      /* Number of free blocks there must be before we... */
++      uint8_t resv_blocks_write;      /* ... allow a normal filesystem write */
++      uint8_t resv_blocks_deletion;   /* ... allow a normal filesystem deletion */
++      uint8_t resv_blocks_gctrigger;  /* ... wake up the GC thread */
++      uint8_t resv_blocks_gcbad;      /* ... pick a block from the bad_list to GC */
++      uint8_t resv_blocks_gcmerge;    /* ... merge pages when garbage collecting */
++
++      uint32_t nospc_dirty_size;
++
++      uint32_t nr_blocks;
+       struct jffs2_eraseblock *blocks;        /* The whole array of blocks. Used for getting blocks 
+                                                * from the offset (blocks[ofs / sector_size]) */
+       struct jffs2_eraseblock *nextblock;     /* The block we're currently filling */
+@@ -58,9 +71,12 @@
+       struct jffs2_eraseblock *gcblock;       /* The block we're currently garbage-collecting */
+       struct list_head clean_list;            /* Blocks 100% full of clean data */
++      struct list_head very_dirty_list;       /* Blocks with lots of dirty space */
+       struct list_head dirty_list;            /* Blocks with some dirty space */
++      struct list_head erasable_list;         /* Blocks which are completely dirty, and need erasing */
++      struct list_head erasable_pending_wbuf_list;    /* Blocks which need erasing but only after the current wbuf is flushed */
+       struct list_head erasing_list;          /* Blocks which are currently erasing */
+-      struct list_head erase_pending_list;    /* Blocks which need erasing */
++      struct list_head erase_pending_list;    /* Blocks which need erasing now */
+       struct list_head erase_complete_list;   /* Blocks which are erased and need the clean marker written to them */
+       struct list_head free_list;             /* Blocks which are free and ready to be used */
+       struct list_head bad_list;              /* Bad blocks. */
+@@ -69,16 +85,35 @@
+       spinlock_t erase_completion_lock;       /* Protect free_list and erasing_list 
+                                                  against erase completion handler */
+       wait_queue_head_t erase_wait;           /* For waiting for erases to complete */
+-      struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
++
++      wait_queue_head_t inocache_wq;
++      struct jffs2_inode_cache **inocache_list;
+       spinlock_t inocache_lock;
+-};
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
+-#else
+-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++      /* Sem to allow jffs2_garbage_collect_deletion_dirent to
++         drop the erase_completion_lock while it's holding a pointer 
++         to an obsoleted node. I don't like this. Alternatives welcomed. */
++      struct semaphore erase_free_sem;
++
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      /* Write-behind buffer for NAND flash */
++      unsigned char *wbuf;
++      uint32_t wbuf_ofs;
++      uint32_t wbuf_len;
++      uint32_t wbuf_pagesize;
++      struct jffs2_inodirty *wbuf_inodes;
++
++      struct rw_semaphore wbuf_sem;   /* Protects the write buffer */
++
++      /* Information about out-of-band area usage... */
++      struct nand_oobinfo *oobinfo;
++      uint32_t badblock_pos;
++      uint32_t fsdata_pos;
++      uint32_t fsdata_len;
+ #endif
+-#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++      /* OS-private pointer for getting back to master superblock info */
++      void *os_priv;
++};
+ #endif /* _JFFS2_FB_SB */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/blktrans.h
+@@ -0,0 +1,72 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux block layer for MTD 'translation layers'.
++ *
++ */
++
++#ifndef __MTD_TRANS_H__
++#define __MTD_TRANS_H__
++
++#include <asm/semaphore.h>
++
++struct hd_geometry;
++struct mtd_info;
++struct mtd_blktrans_ops;
++struct file;
++struct inode;
++
++struct mtd_blktrans_dev {
++      struct mtd_blktrans_ops *tr;
++      struct list_head list;
++      struct mtd_info *mtd;
++      struct semaphore sem;
++      int devnum;
++      int blksize;
++      unsigned long size;
++      int readonly;
++      void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
++};
++
++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
++
++struct mtd_blktrans_ops {
++      char *name;
++      int major;
++      int part_bits;
++
++      /* Access functions */
++      int (*readsect)(struct mtd_blktrans_dev *dev,
++                  unsigned long block, char *buffer);
++      int (*writesect)(struct mtd_blktrans_dev *dev,
++                   unsigned long block, char *buffer);
++
++      /* Block layer ioctls */
++      int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
++      int (*flush)(struct mtd_blktrans_dev *dev);
++
++      /* Called with mtd_table_mutex held; no race with add/remove */
++      int (*open)(struct mtd_blktrans_dev *dev);
++      int (*release)(struct mtd_blktrans_dev *dev);
++
++      /* Called on {de,}registration and on subsequent addition/removal
++         of devices, with mtd_table_mutex held. */
++      void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);
++      void (*remove_dev)(struct mtd_blktrans_dev *dev);
++
++      struct list_head devs;
++      struct list_head list;
++      struct module *owner;
++
++      struct mtd_blkcore_priv *blkcore_priv;
++};
++
++extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++                               
++
++#endif /* __MTD_TRANS_H__ */
+--- linux-2.4.21/include/linux/mtd/cfi.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/cfi.h
+@@ -1,211 +1,86 @@
+ /* Common Flash Interface structures 
+  * See http://support.intel.com/design/flash/technote/index.htm
+- * $Id$
++ * $Id$
+  */
+ #ifndef __MTD_CFI_H__
+ #define __MTD_CFI_H__
+ #include <linux/config.h>
++#include <linux/version.h>
+ #include <linux/delay.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/flashchip.h>
++#include <linux/mtd/map.h>
+ #include <linux/mtd/cfi_endian.h>
+-/*
+- * You can optimize the code size and performance by defining only 
+- * the geometry(ies) available on your hardware.
+- * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
+- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
+- *
+- * By default, all (known) geometries are supported.
+- */
+-
+-#ifndef CONFIG_MTD_CFI_GEOMETRY
+-
+-/* The default case - support all but 64-bit, which has
+-   a performance penalty */
+-
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#define CFIDEV_INTERLEAVE_4 (4)
+-
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#define CFIDEV_BUSWIDTH_4 (4)
+-
+-typedef __u32 cfi_word;
+-
+-#else
+-
+-/* Explicitly configured buswidth/interleave support */
+-
+ #ifdef CONFIG_MTD_CFI_I1
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I2
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I4
+-#define CFIDEV_INTERLEAVE_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I8
+-#define CFIDEV_INTERLEAVE_8 (8)
+-#endif
+-
+-#ifdef CONFIG_MTD_CFI_B1
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B2
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B4
+-#define CFIDEV_BUSWIDTH_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B8
+-#define CFIDEV_BUSWIDTH_8 (8)
+-#endif
+-
+-/* pick the largest necessary */
+-#ifdef CONFIG_MTD_CFI_B8
+-typedef __u64 cfi_word;
+-
+-/* This only works if asm/io.h is included first */
+-#ifndef __raw_readll
+-#define __raw_readll(addr)    (*(volatile __u64 *)(addr))
+-#endif
+-#ifndef __raw_writell
+-#define __raw_writell(v, addr)        (*(volatile __u64 *)(addr) = (v))
+-#endif
+-#define CFI_WORD_64
+-#else  /* CONFIG_MTD_CFI_B8 */
+-/* All others can use 32-bits. It's probably more efficient than
+-   the smaller types anyway */
+-typedef __u32 cfi_word;
+-#endif /* CONFIG_MTD_CFI_B8 */
+-
+-#endif
+-
+-/*
+- * The following macros are used to select the code to execute:
+- *   cfi_buswidth_is_*()
+- *   cfi_interleave_is_*()
+- *   [where * is either 1, 2, 4, or 8]
+- * Those macros should be used with 'if' statements.  If only one of few
+- * geometry arrangements are selected, they expand to constants thus allowing
+- * the compiler (most of them being 0) to optimize away all the unneeded code,
+- * while still validating the syntax (which is not possible with embedded 
+- * #if ... #endif constructs).
+- * The exception to this is the 64-bit versions, which need an extension
+- * to the cfi_word type, and cause compiler warnings about shifts being
+- * out of range.
+- */
+-
+-#ifdef CFIDEV_INTERLEAVE_1
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+-# else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
+-# endif
+-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
++#define cfi_interleave(cfi) 1
++#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
+ #else
+-# define cfi_interleave_is_1() (0)
++#define cfi_interleave_is_1(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_2
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I2
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
++#  define cfi_interleave(cfi) 2
+ # endif
+-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
++#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
+ #else
+-# define cfi_interleave_is_2() (0)
++#define cfi_interleave_is_2(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_4
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I4
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
++#  define cfi_interleave(cfi) 4
+ # endif
+-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
++#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
+ #else
+-# define cfi_interleave_is_4() (0)
++#define cfi_interleave_is_4(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_8
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I8
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
++#  define cfi_interleave(cfi) 8
+ # endif
+-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
++#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
+ #else
+-# define cfi_interleave_is_8() (0)
++#define cfi_interleave_is_8(cfi) (0)
+ #endif
+-#ifndef CFIDEV_INTERLEAVE
+-#error You must define at least one interleave to support!
++static inline int cfi_interleave_supported(int i)
++{
++      switch (i) {
++#ifdef CONFIG_MTD_CFI_I1
++      case 1:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_1
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
+-# endif
+-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
+-#else
+-# define cfi_buswidth_is_1() (0)
++#ifdef CONFIG_MTD_CFI_I2
++      case 2:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_2
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
+-# endif
+-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
+-#else
+-# define cfi_buswidth_is_2() (0)
++#ifdef CONFIG_MTD_CFI_I4
++      case 4:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_4
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
+-# endif
+-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
+-#else
+-# define cfi_buswidth_is_4() (0)
++#ifdef CONFIG_MTD_CFI_I8
++      case 8:
+ #endif
++              return 1;
+-#ifdef CFIDEV_BUSWIDTH_8
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
+-# endif
+-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
+-#else
+-# define cfi_buswidth_is_8() (0)
+-#endif
++      default:
++              return 0;
++      }
++}
+-#ifndef CFIDEV_BUSWIDTH
+-#error You must define at least one bus width to support!
+-#endif
+ /* NB: these values must represents the number of bytes needed to meet the 
+  *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes. 
+@@ -222,88 +97,138 @@
+ /* Basic Query Structure */
+ struct cfi_ident {
+-  __u8  qry[3];
+-  __u16 P_ID;
+-  __u16 P_ADR;
+-  __u16 A_ID;
+-  __u16 A_ADR;
+-  __u8  VccMin;
+-  __u8  VccMax;
+-  __u8  VppMin;
+-  __u8  VppMax;
+-  __u8  WordWriteTimeoutTyp;
+-  __u8  BufWriteTimeoutTyp;
+-  __u8  BlockEraseTimeoutTyp;
+-  __u8  ChipEraseTimeoutTyp;
+-  __u8  WordWriteTimeoutMax;
+-  __u8  BufWriteTimeoutMax;
+-  __u8  BlockEraseTimeoutMax;
+-  __u8  ChipEraseTimeoutMax;
+-  __u8  DevSize;
+-  __u16 InterfaceDesc;
+-  __u16 MaxBufWriteSize;
+-  __u8  NumEraseRegions;
+-  __u32 EraseRegionInfo[0]; /* Not host ordered */
++      uint8_t  qry[3];
++      uint16_t P_ID;
++      uint16_t P_ADR;
++      uint16_t A_ID;
++      uint16_t A_ADR;
++      uint8_t  VccMin;
++      uint8_t  VccMax;
++      uint8_t  VppMin;
++      uint8_t  VppMax;
++      uint8_t  WordWriteTimeoutTyp;
++      uint8_t  BufWriteTimeoutTyp;
++      uint8_t  BlockEraseTimeoutTyp;
++      uint8_t  ChipEraseTimeoutTyp;
++      uint8_t  WordWriteTimeoutMax;
++      uint8_t  BufWriteTimeoutMax;
++      uint8_t  BlockEraseTimeoutMax;
++      uint8_t  ChipEraseTimeoutMax;
++      uint8_t  DevSize;
++      uint16_t InterfaceDesc;
++      uint16_t MaxBufWriteSize;
++      uint8_t  NumEraseRegions;
++      uint32_t EraseRegionInfo[0]; /* Not host ordered */
+ } __attribute__((packed));
+ /* Extended Query Structure for both PRI and ALT */
+ struct cfi_extquery {
+-  __u8  pri[3];
+-  __u8  MajorVersion;
+-  __u8  MinorVersion;
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
+ } __attribute__((packed));
+ /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
+ struct cfi_pri_intelext {
+-  __u8  pri[3];
+-  __u8  MajorVersion;
+-  __u8  MinorVersion;
+-  __u32 FeatureSupport;
+-  __u8  SuspendCmdSupport;
+-  __u16 BlkStatusRegMask;
+-  __u8  VccOptimal;
+-  __u8  VppOptimal;
+-  __u8  NumProtectionFields;
+-  __u16 ProtRegAddr;
+-  __u8  FactProtRegSize;
+-  __u8  UserProtRegSize;
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
++      uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
++                                  block follows - FIXME - not currently supported */
++      uint8_t  SuspendCmdSupport;
++      uint16_t BlkStatusRegMask;
++      uint8_t  VccOptimal;
++      uint8_t  VppOptimal;
++      uint8_t  NumProtectionFields;
++      uint16_t ProtRegAddr;
++      uint8_t  FactProtRegSize;
++      uint8_t  UserProtRegSize;
++      uint8_t  extra[0];
++} __attribute__((packed));
++
++struct cfi_intelext_otpinfo {
++      uint32_t ProtRegAddr;
++      uint16_t FactGroups;
++      uint8_t  FactProtRegSize;
++      uint16_t UserGroups;
++      uint8_t  UserProtRegSize;
++} __attribute__((packed));
++
++struct cfi_intelext_blockinfo {
++      uint16_t NumIdentBlocks;
++      uint16_t BlockSize;
++      uint16_t MinBlockEraseCycles;
++      uint8_t  BitsPerCell;
++      uint8_t  BlockCap;
++} __attribute__((packed));
++
++struct cfi_intelext_regioninfo {
++      uint16_t NumIdentPartitions;
++      uint8_t  NumOpAllowed;
++      uint8_t  NumOpAllowedSimProgMode;
++      uint8_t  NumOpAllowedSimEraMode;
++      uint8_t  NumBlockTypes;
++      struct cfi_intelext_blockinfo BlockTypes[1];
++} __attribute__((packed));
++
++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
++
++struct cfi_pri_amdstd {
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
++      uint8_t  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
++      uint8_t  EraseSuspend;
++      uint8_t  BlkProt;
++      uint8_t  TmpBlkUnprotect;
++      uint8_t  BlkProtUnprot;
++      uint8_t  SimultaneousOps;
++      uint8_t  BurstMode;
++      uint8_t  PageMode;
++      uint8_t  VppMin;
++      uint8_t  VppMax;
++      uint8_t  TopBottom;
+ } __attribute__((packed));
+ struct cfi_pri_query {
+-  __u8  NumFields;
+-  __u32 ProtField[1]; /* Not host ordered */
++      uint8_t  NumFields;
++      uint32_t ProtField[1]; /* Not host ordered */
+ } __attribute__((packed));
+ struct cfi_bri_query {
+-  __u8  PageModeReadCap;
+-  __u8  NumFields;
+-  __u32 ConfField[1]; /* Not host ordered */
++      uint8_t  PageModeReadCap;
++      uint8_t  NumFields;
++      uint32_t ConfField[1]; /* Not host ordered */
+ } __attribute__((packed));
+-#define P_ID_NONE 0
+-#define P_ID_INTEL_EXT 1
+-#define P_ID_AMD_STD 2
+-#define P_ID_INTEL_STD 3
+-#define P_ID_AMD_EXT 4
+-#define P_ID_MITSUBISHI_STD 256
+-#define P_ID_MITSUBISHI_EXT 257
+-#define P_ID_RESERVED 65535
++#define P_ID_NONE               0x0000
++#define P_ID_INTEL_EXT          0x0001
++#define P_ID_AMD_STD            0x0002
++#define P_ID_INTEL_STD          0x0003
++#define P_ID_AMD_EXT            0x0004
++#define P_ID_WINBOND            0x0006
++#define P_ID_ST_ADV             0x0020
++#define P_ID_MITSUBISHI_STD     0x0100
++#define P_ID_MITSUBISHI_EXT     0x0101
++#define P_ID_SST_PAGE           0x0102
++#define P_ID_INTEL_PERFORMANCE  0x0200
++#define P_ID_INTEL_DATA         0x0210
++#define P_ID_RESERVED           0xffff
+ #define CFI_MODE_CFI  1
+ #define CFI_MODE_JEDEC        0
+ struct cfi_private {
+-      __u16 cmdset;
++      uint16_t cmdset;
+       void *cmdset_priv;
+       int interleave;
+       int device_type;
+       int cfi_mode;           /* Are we a JEDEC device pretending to be CFI? */
+       int addr_unlock1;
+       int addr_unlock2;
+-      int fast_prog;
+       struct mtd_info *(*cmdset_setup)(struct map_info *);
+       struct cfi_ident *cfiq; /* For now only one. We insist that all devs
+                                 must be of the same type. */
+@@ -314,107 +239,81 @@
+       struct flchip chips[0];  /* per-chip data structure for each chip */
+ };
+-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
+-
+ /*
+  * Returns the command address according to the given geometry.
+  */
+-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
++static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
+ {
+       return (cmd_ofs * type) * interleave;
+ }
+ /*
+- * Transforms the CFI command for the given geometry (bus width & interleave.
++ * Transforms the CFI command for the given geometry (bus width & interleave).
++ * It looks too long to be inline, but in the common case it should almost all
++ * get optimised away. 
+  */
+-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
++static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
+ {
+-      cfi_word val = 0;
++      map_word val = { {0} };
++      int wordwidth, words_per_bus, chip_mode, chips_per_word;
++      unsigned long onecmd;
++      int i;
+-      if (cfi_buswidth_is_1()) {
+-              /* 1 x8 device */
+-              val = cmd;
+-      } else if (cfi_buswidth_is_2()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x16 device in x16 mode */
+-                      val = cpu_to_cfi16(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 (x8, x16 or x32) devices in x8 mode */
+-                      val = cpu_to_cfi16((cmd << 8) | cmd);
+-              }
+-      } else if (cfi_buswidth_is_4()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x32 device in x32 mode */
+-                      val = cpu_to_cfi32(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 x16 device in x16 mode */
+-                      val = cpu_to_cfi32((cmd << 16) | cmd);
+-              } else if (cfi_interleave_is_4()) {
+-                      /* 4 (x8, x16 or x32) devices in x8 mode */
+-                      val = (cmd << 16) | cmd;
+-                      val = cpu_to_cfi32((val << 8) | val);
+-              }
+-#ifdef CFI_WORD_64
+-      } else if (cfi_buswidth_is_8()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x64 device in x64 mode */
+-                      val = cpu_to_cfi64(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 x32 device in x32 mode */
+-                      val = cmd;
+-                      val = cpu_to_cfi64((val << 32) | val);
+-              } else if (cfi_interleave_is_4()) {
+-                      /* 4 (x16, x32 or x64) devices in x16 mode */
+-                      val = (cmd << 16) | cmd;
+-                      val = cpu_to_cfi64((val << 32) | val);
+-              } else if (cfi_interleave_is_8()) {
+-                      /* 8 (x8, x16 or x32) devices in x8 mode */
+-                      val = (cmd << 8) | cmd;
+-                      val = (val << 16) | val;
+-                      val = (val << 32) | val;
+-                      val = cpu_to_cfi64(val);
+-              }
+-#endif /* CFI_WORD_64 */
++      /* We do it this way to give the compiler a fighting chance 
++         of optimising away all the crap for 'bankwidth' larger than
++         an unsigned long, in the common case where that support is
++         disabled */
++      if (map_bankwidth_is_large(map)) {
++              wordwidth = sizeof(unsigned long);
++              words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
++      } else {
++              wordwidth = map_bankwidth(map);
++              words_per_bus = 1;
+       }
+-      return val;
+-}
+-#define CMD(x)  cfi_build_cmd((x), map, cfi)
+-/*
+- * Read a value according to the bus width.
+- */
++      chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
++      chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+-{
+-      if (cfi_buswidth_is_1()) {
+-              return map->read8(map, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              return map->read16(map, addr);
+-      } else if (cfi_buswidth_is_4()) {
+-              return map->read32(map, addr);
+-      } else if (cfi_buswidth_is_8()) {
+-              return map->read64(map, addr);
+-      } else {
+-              return 0;
++      /* First, determine what the bit-pattern should be for a single
++         device, according to chip mode and endianness... */
++      switch (chip_mode) {
++      default: BUG();
++      case 1:
++              onecmd = cmd;
++              break;
++      case 2:
++              onecmd = cpu_to_cfi16(cmd);
++              break;
++      case 4:
++              onecmd = cpu_to_cfi32(cmd);
++              break;
+       }
+-}
+-/*
+- * Write a value according to the bus width.
+- */
++      /* Now replicate it across the size of an unsigned long, or 
++         just to the bus width as appropriate */
++      switch (chips_per_word) {
++      default: BUG();
++#if BITS_PER_LONG >= 64
++      case 8:
++              onecmd |= (onecmd << (chip_mode * 32));
++#endif
++      case 4:
++              onecmd |= (onecmd << (chip_mode * 16));
++      case 2:
++              onecmd |= (onecmd << (chip_mode * 8));
++      case 1:
++              ;
++      }
+-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
+-{
+-      if (cfi_buswidth_is_1()) {
+-              map->write8(map, val, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              map->write16(map, val, addr);
+-      } else if (cfi_buswidth_is_4()) {
+-              map->write32(map, val, addr);
+-      } else if (cfi_buswidth_is_8()) {
+-              map->write64(map, val, addr);
++      /* And finally, for the multi-word case, replicate it 
++         in all words in the structure */
++      for (i=0; i < words_per_bus; i++) {
++              val.x[i] = onecmd;
+       }
++
++      return val;
+ }
++#define CMD(x)  cfi_build_cmd((x), map, cfi)
+ /*
+  * Sends a CFI command to a bank of flash for the given geometry.
+@@ -423,50 +322,47 @@
+  * If prev_val is non-null, it will be set to the value at the command address,
+  * before the command was written.
+  */
+-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
++static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+                               struct map_info *map, struct cfi_private *cfi,
+-                              int type, cfi_word *prev_val)
++                              int type, map_word *prev_val)
+ {
+-      cfi_word val;
+-      __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
++      map_word val;
++      uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+       val = cfi_build_cmd(cmd, map, cfi);
+       if (prev_val)
+-              *prev_val = cfi_read(map, addr);
++              *prev_val = map_read(map, addr);
+-      cfi_write(map, val, addr);
++      map_write(map, val, addr);
+       return addr - base;
+ }
+-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
++static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
+ {
+-      if (cfi_buswidth_is_1()) {
+-              return map->read8(map, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              return cfi16_to_cpu(map->read16(map, addr));
+-      } else if (cfi_buswidth_is_4()) {
+-              return cfi32_to_cpu(map->read32(map, addr));
+-      } else if (cfi_buswidth_is_8()) {
+-              return cfi64_to_cpu(map->read64(map, addr));
++      map_word val = map_read(map, addr);
++
++      if (map_bankwidth_is_1(map)) {
++              return val.x[0];
++      } else if (map_bankwidth_is_2(map)) {
++              return cfi16_to_cpu(val.x[0]);
+       } else {
+-              return 0;
++              /* No point in a 64-bit byteswap since that would just be
++                 swapping the responses from different chips, and we are
++                 only interested in one chip (a representative sample) */
++              return cfi32_to_cpu(val.x[0]);
+       }
+ }
+ static inline void cfi_udelay(int us)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+-      unsigned long t = us * HZ / 1000000;
+-      if (t) {
+-              set_current_state(TASK_UNINTERRUPTIBLE);
+-              schedule_timeout(t);
+-              return;
+-      }
+-#endif
++      if (us >= 1000) {
++              msleep((us+999)/1000);
++      } else {
+       udelay(us);
+       cond_resched();
++      }
+ }
+ static inline void cfi_spin_lock(spinlock_t *mutex)
+@@ -479,5 +375,28 @@
+       spin_unlock_bh(mutex);
+ }
++struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
++                           const char* name);
++struct cfi_fixup {
++      uint16_t mfr;
++      uint16_t id;
++      void (*fixup)(struct mtd_info *mtd, void* param);
++      void* param;
++};
++
++#define CFI_MFR_ANY 0xffff
++#define CFI_ID_ANY  0xffff
++
++#define CFI_MFR_AMD 0x0001
++#define CFI_MFR_ST  0x0020    /* STMicroelectronics */
++
++void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
++
++typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
++                            unsigned long adr, int len, void *thunk);
++
++int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
++      loff_t ofs, size_t len, void *thunk);
++
+ #endif /* __MTD_CFI_H__ */
+--- linux-2.4.21/include/linux/mtd/compatmac.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/compatmac.h
+@@ -1,583 +1,223 @@
+-
+ /*
+- * mtd/include/compatmac.h
+- *
+- * $Id$
++ * $Id$
+  *
+  * Extensions and omissions from the normal 'linux/compatmac.h'
+  * files. hopefully this will end up empty as the 'real' one 
+  * becomes fully-featured.
+  */
+-
+-/* First, include the parts which the kernel is good enough to provide 
+- * to us 
+- */
+-   
+ #ifndef __LINUX_MTD_COMPATMAC_H__
+ #define __LINUX_MTD_COMPATMAC_H__
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#ifndef LINUX_VERSION_CODE
+ #include <linux/version.h>
+-#endif
+-
+-#ifndef VERSION_CODE
+-#  define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+-#endif
+-#ifndef KERNEL_VERSION
+-#  define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
+-#  error "This kernel is too old: not supported by this file"
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+-#include <linux/types.h> /* used later in this header */
+-
+-#define memcpy_fromio(a,b,c)    memcpy((a),(void *)(b),(c))
+-#define memcpy_toio(a,b,c)      memcpy((void *)(a),(b),(c))
+-
+-typedef struct wait_queue * wait_queue_head_t;
+-
+-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL}
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
+-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX
+-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED
+-
+-/* from sysdep-2.1.h */
+-#  include <asm/segment.h>
+-#  define access_ok(t,a,sz)           (verify_area((t),(a),(sz)) ? 0 : 1)
+-#  define verify_area_20              verify_area
+-#  define copy_to_user(t,f,n)         (memcpy_tofs(t,f,n), 0)
+-#  define __copy_to_user(t,f,n)       copy_to_user((t),(f),(n))
+-#  define copy_to_user_ret(t,f,n,r)   copy_to_user((t),(f),(n))
+-#  define copy_from_user(t,f,n)       (memcpy_fromfs((t),(f),(n)), 0)
+-#  define __copy_from_user(t,f,n)     copy_from_user((t),(f),(n))
+-#  define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
+-//xxx #  define PUT_USER(val,add)           (put_user((val),(add)), 0)
+-#  define Put_user(val,add)           (put_user((val),(add)), 0)
+-#  define __PUT_USER(val,add)         PUT_USER((val),(add))
+-#  define PUT_USER_RET(val,add,ret)   PUT_USER((val),(add))
+-#  define GET_USER(dest,add)          ((dest)=get_user((add)), 0)
+-#  define __GET_USER(dest,add)        GET_USER((dest),(add))
+-#  define GET_USER_RET(dest,add,ret)  GET_USER((dest),(add))
+-
+-#define ioremap(offset,size) vremap(offset,size)
+-#define iounmap(adr)  /* */
+-
+-#define EXPORT_SYMBOL(s) /* */
+-#define EXPORT_SYMBOL_NOVERS(s) /* */
+-
+-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10)
+-
+-#  include <asm/byteorder.h>
+-#  ifdef __LITTLE_ENDIAN
+-#    define cpu_to_le16(x) (x)
+-#    define cpu_to_le32(x) (x)
+-#    define cpu_to_be16(x) htons((x))
+-#    define cpu_to_be32(x) htonl((x))
+-#  else
+-#    define cpu_to_be16(x) (x)
+-#    define cpu_to_be32(x) (x)
+-     extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+-     extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) |
+-             ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}
+-#  endif
+-
+-#  define le16_to_cpu(x)  cpu_to_le16(x)
+-#  define le32_to_cpu(x)  cpu_to_le32(x)
+-#  define be16_to_cpu(x)  cpu_to_be16(x)
+-#  define be32_to_cpu(x)  cpu_to_be32(x)
+-
+-#endif
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43)
+-#  define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+-#  define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+-#  define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+-#  define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+-
+-   extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+-   extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+-   extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+-   extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+-
+-#  define le16_to_cpup(x) cpu_to_le16p(x)
+-#  define le32_to_cpup(x) cpu_to_le32p(x)
+-#  define be16_to_cpup(x) cpu_to_be16p(x)
+-#  define be32_to_cpup(x) cpu_to_be32p(x)
+-
+-#  define le16_to_cpus(x) cpu_to_le16s(x)
+-#  define le32_to_cpus(x) cpu_to_le32s(x)
+-#  define be16_to_cpus(x) cpu_to_be16s(x)
+-#  define be32_to_cpus(x) cpu_to_be32s(x)
+-#endif
+-
+-// from 2.2, linux/types.h
+-#ifndef __BIT_TYPES_DEFINED__
+-#define __BIT_TYPES_DEFINED__
+-
+-typedef         __u8            u_int8_t;
+-typedef         __s8            int8_t;
+-typedef         __u16           u_int16_t;
+-typedef         __s16           int16_t;
+-typedef         __u32           u_int32_t;
+-typedef         __s32           int32_t;
+-
+-#endif /* !(__BIT_TYPES_DEFINED__) */
+-
+-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+-  typedef struct { } spinlock_t;
+-  #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
+-#else
+-  typedef struct { int gcc_is_buggy; } spinlock_t;
+-  #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
+-#endif
+-
+-#define spin_lock_init(lock)    do { } while(0)
+-#define spin_lock(lock)         (void)(lock) /* Not "unused variable". */
+-#define spin_trylock(lock)      (1)
+-#define spin_unlock_wait(lock)  do { } while(0)
+-#define spin_unlock(lock)       do { } while(0)
+-#define spin_lock_irq(lock)     cli()
+-#define spin_unlock_irq(lock)   sti()
+-
+-#define spin_lock_irqsave(lock, flags) \
+-        do { save_flags(flags); cli(); } while (0)
+-#define spin_unlock_irqrestore(lock, flags) \
+-        restore_flags(flags)
+-
+-// Doesn't work when tqueue.h is included. 
+-// #define queue_task                   queue_task_irq_off
+-#define tty_flip_buffer_push(tty)    queue_task_irq_off(&tty->flip.tqueue, &tq_timer)
+-#define signal_pending(current)      (current->signal & ~current->blocked)
+-#define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
+-#define time_after(t1,t2)            (((long)t1-t2) > 0)
+-
+-#else
+-  #include <linux/compatmac.h>
+-#endif  // LINUX_VERSION_CODE < 0x020100
+-
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-#include <linux/vmalloc.h>
+-#endif
+-
+-/* Modularization issues */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18)
+-#  define __USE_OLD_SYMTAB__
+-#  define EXPORT_NO_SYMBOLS register_symtab(NULL);
+-#  define REGISTER_SYMTAB(tab) register_symtab(tab)
+-#else
+-#  define REGISTER_SYMTAB(tab) /* nothing */
+-#endif
+-#ifdef __USE_OLD_SYMTAB__
+-#  define __MODULE_STRING(s)         /* nothing */
+-#  define MODULE_PARM(v,t)           /* nothing */
+-#  define MODULE_PARM_DESC(v,t)      /* nothing */
+-#  define MODULE_AUTHOR(n)           /* nothing */
+-#  define MODULE_DESCRIPTION(d)      /* nothing */
+-#  define MODULE_SUPPORTED_DEVICE(n) /* nothing */
+-#endif
+-
+-/*
+- * "select" changed in 2.1.23. The implementation is twin, but this
+- * header is new
+- */
+-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22)
+-#  include <linux/poll.h>
+-#else
+-#  define __USE_OLD_SELECT__
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
++#error "This kernel is too old: not supported by this file"
+ #endif
+-/* Other change in the fops are solved using pseudo-types */
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-#  define lseek_t      long long
+-#  define lseek_off_t  long long
+-#else
+-#  define lseek_t      int
+-#  define lseek_off_t  off_t
+-#endif
++      /* O(1) scheduler stuff. */
+-/* changed the prototype of read/write */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
++#include <linux/sched.h>
++static inline void __recalc_sigpending(void)
++{
++      recalc_sigpending(current);
++}
++#undef recalc_sigpending
++#define recalc_sigpending() __recalc_sigpending ()
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__)
+-# define count_t unsigned long
+-# define read_write_t long
+-#else
+-# define count_t int
+-# define read_write_t int
++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31)
+-# define release_t void
+-#  define release_return(x) return
+-#else
+-#  define release_t int
+-#  define release_return(x) return (x)
+-#endif
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#define __exit
+-#endif
+-#if LINUX_VERSION_CODE < 0x20200
+-#define __init
+-#else
+-#include <linux/init.h>
+-#endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0)
+-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0)
+-#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-#define RQFUNC_ARG void
+-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+-#else
+-#define RQFUNC_ARG request_queue_t *q
++#ifndef yield
++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32)
+-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0)
+-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn)
+-#define blk_init_queue(q, rq) do {q = rq;} while(0)
++#ifndef minor
++#define major(d) (MAJOR(to_kdev_t(d)))
++#define minor(d) (MINOR(to_kdev_t(d)))
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-#ifdef CONFIG_MODULES
+-#define __MOD_INC_USE_COUNT(mod)                                        \
+-        (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
+-#define __MOD_DEC_USE_COUNT(mod)                                        \
+-        (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
+-#else
+-#define __MOD_INC_USE_COUNT(mod)
+-#define __MOD_DEC_USE_COUNT(mod)
+-#endif
++#ifndef mk_kdev
++#define mk_kdev(ma,mi) MKDEV(ma,mi)
++#define kdev_t_to_nr(x)       (x)
+ #endif
++#define need_resched() (current->need_resched)
++#define cond_resched() do { if need_resched() { yield(); } } while(0)
+-#ifndef HAVE_INTER_MODULE
+-static inline void *inter_module_get(char *x) {return NULL;}
+-static inline void *inter_module_get_request(char *x, char *y) {return NULL;}
+-static inline void inter_module_put(const char *x) {}
+-static inline void inter_module_register(const char *x, struct module *y, const void *z) {}
+-static inline void inter_module_unregister(const char *x) {}
++#endif /* < 2.4.20 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define iminor(i) minor((i)->i_rdev)
++#define imajor(i) major((i)->i_rdev)
++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) )
++#define old_decode_dev(rdev)  (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff)))
++#define old_valid_dev(d) (1)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61)
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
++#include <linux/sched.h>
++#ifdef __rh_config_h__
++#define sigmask_lock sighand->siglock
++#define sig sighand
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-
+-static inline int try_inc_mod_count(struct module *mod)
++static inline void __daemonize_modvers(void)
+ {
+-#ifdef CONFIG_MODULES
+-      if (mod)
+-              __MOD_INC_USE_COUNT(mod);
+-#endif
+-      return 1;
+-}
+-#endif
+-
+-
+-/* Yes, I'm aware that it's a fairly ugly hack.
+-   Until the __constant_* macros appear in Linus' own kernels, this is
+-   the way it has to be done.
+- DW 19/1/00
+- */
+-
+-#include <asm/byteorder.h>
+-
+-#ifndef __constant_cpu_to_le16
+-
+-#ifdef __BIG_ENDIAN
+-#define __constant_cpu_to_le64(x) ___swab64((x))
+-#define __constant_le64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_le32(x) ___swab32((x))
+-#define __constant_le32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_le16(x) ___swab16((x))
+-#define __constant_le16_to_cpu(x) ___swab16((x))
+-#define __constant_cpu_to_be64(x) ((__u64)(x))
+-#define __constant_be64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_be32(x) ((__u32)(x))
+-#define __constant_be32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_be16(x) ((__u16)(x))
+-#define __constant_be16_to_cpu(x) ((__u16)(x))
+-#else
+-#ifdef __LITTLE_ENDIAN
+-#define __constant_cpu_to_le64(x) ((__u64)(x))
+-#define __constant_le64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_le32(x) ((__u32)(x))
+-#define __constant_le32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_le16(x) ((__u16)(x))
+-#define __constant_le16_to_cpu(x) ((__u16)(x))
+-#define __constant_cpu_to_be64(x) ___swab64((x))
+-#define __constant_be64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_be32(x) ___swab32((x))
+-#define __constant_be32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_be16(x) ___swab16((x))
+-#define __constant_be16_to_cpu(x) ___swab16((x))
+-#else
+-#error No (recognised) endianness defined (unless it,s PDP)
+-#endif /* __LITTLE_ENDIAN */
+-#endif /* __BIG_ENDIAN */
+-
+-#endif /* ifndef __constant_cpu_to_le16 */
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-  #define mod_init_t int  __init
+-  #define mod_exit_t void  
+-#else
+-  #define mod_init_t static int __init
+-  #define mod_exit_t static void __exit
+-#endif
++      daemonize();
+-#ifndef THIS_MODULE
+-#ifdef MODULE
+-#define THIS_MODULE (&__this_module)
+-#else
+-#define THIS_MODULE (NULL)
+-#endif
+-#endif
++      spin_lock_irq(&current->sigmask_lock);
++      sigfillset(&current->blocked);
++      recalc_sigpending();
++      spin_unlock_irq(&current->sigmask_lock);
++}
++#undef daemonize
++#define daemonize(fmt, ...) do {                                              \
++      snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__);     \
++      __daemonize_modvers();                                                  \
++      } while(0)
+-#if LINUX_VERSION_CODE < 0x20300
+-#include <linux/interrupt.h>
+-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0)
+-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0)
+-#else
+-#include <asm/softirq.h>
+-#include <linux/spinlock.h>
+-#endif
++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
++{
++      unsigned long flags;
++      unsigned long ret;
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define set_current_state(state_value)                        \
+-        do { current->state = (state_value); } while (0)
+-#endif
++      spin_lock_irqsave(&current->sigmask_lock, flags);
++      ret = dequeue_signal(mask, info);
++      spin_unlock_irqrestore(&current->sigmask_lock, flags);
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) 
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
++      return ret;
++}
+-      if (do_sync)
+-              fsync_dev(dev);
++static inline int allow_signal(int sig)
++{
++      if (sig < 1 || sig > _NSIG)
++              return -EINVAL;
+       
+-      invalidate_buffers(dev);
++        spin_lock_irq(&current->sigmask_lock);
++      sigdelset(&current->blocked, sig);
++      recalc_sigpending();
++      /* Make sure the kernel neither eats it now converts to SIGKILL */
++      current->sig->action[sig-1].sa.sa_handler = (void *)2;
++      spin_unlock_irq(&current->sigmask_lock);
+       return 0;
+ }
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
+-      struct super_block *sb = get_super(dev);
+-      int res = 0;
+-
+-      if (do_sync)
+-              fsync_dev(dev);
++static inline int disallow_signal(int sig)
++{
++      if (sig < 1 || sig > _NSIG)
++              return -EINVAL;
+       
+-      if (sb)
+-              res = invalidate_inodes(sb);
++      spin_lock_irq(&current->sigmask_lock);
++      sigaddset(&current->blocked, sig);
++      recalc_sigpending();
+-      invalidate_buffers(dev);
+-      return res;
++      current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
++      spin_unlock_irq(&current->sigmask_lock);
++      return 0;
+ }
++
++#define PF_FREEZE 0
++#define refrigerator(x) do { ; } while(0)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+-#undef min
+-#undef max
+-#undef min_t
+-#undef max_t
+-/*
+- * min()/max() macros that also do
+- * strict type-checking.. See the
+- * "unnecessary" pointer comparison.
+- */
+-#define min(x,y) ({ \
+-        const typeof(x) _x = (x);       \
+-        const typeof(y) _y = (y);       \
+-        (void) (&_x == &_y);            \
+-        _x < _y ? _x : _y; })
++      /* Module bits */
+-#define max(x,y) ({ \
+-        const typeof(x) _x = (x);       \
+-        const typeof(y) _y = (y);       \
+-        (void) (&_x == &_y);            \
+-        _x > _y ? _x : _y; })
+-/*
+- * ..and if you can't take the strict
+- * types, you can specify one yourself.
+- *
+- * Or not use min/max at all, of course.
+- */
+-#define min_t(type,x,y) \
+-        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+-#define max_t(type,x,y) \
+-        ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
++#define try_module_get(m) try_inc_mod_count(m)
++#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0)
++#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0)
++#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
+-struct completion {
+-      struct semaphore s;
+-};
+-#define complete(c) up(&(c)->s)
+-#define wait_for_completion(c) down(&(c)->s)
+-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
++      /* Random filesystem stuff, only for JFFS2 really */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
++#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9)
+-/* This came later */
+-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
++#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
+-
+-#include <linux/genhd.h>
+-
+-static inline void add_gendisk(struct gendisk *gp)
+-{
+-      gp->next = gendisk_head;
+-      gendisk_head = gp;
+-}
+-
+-static inline void del_gendisk(struct gendisk *gp)
+-{
+-      struct gendisk *gd, **gdp;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++#define get_seconds() CURRENT_TIME
++#endif
+-      for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+-              if (*gdp == gp) {
+-                      gd = *gdp; *gdp = gd->next;
+-                      break;
+-              }
+-}
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
++#define generic_file_readonly_mmap generic_file_mmap
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
+-#define module_init(func)             \
+-mod_init_t init_module(void) {                \
+-      return func();                  \
+-}
++#include <linux/kmod.h>
++#include <linux/string.h>
+-#define module_exit(func)             \
+-mod_exit_t cleanup_module(void) {     \
+-      return func();                  \
++static inline char *strlcpy(char *dest, const char *src, int len)
++{
++      dest[len-1] = 0;
++      return strncpy(dest, src, len-1);
+ }
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
+-#define MODULE_LICENSE(x) /* */
+-#endif
+-/* Removed for 2.4.21 kernel. This really should have been renamed
+-   when it was changed -- this is a PITA */
+-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#include <linux/sched.h>
+-static inline void __recalc_sigpending(void)
++static inline int do_old_request_module(const char *mod)
+ {
+-      recalc_sigpending(current);
++      return request_module(mod);
+ }
+-#undef recalc_sigpending
+-#define recalc_sigpending() __recalc_sigpending ()
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+-#endif
++#undef request_module
++#define request_module(fmt, ...) \
++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3)
+-#define need_resched() (current->need_resched)
+-#define cond_resched() do { if need_resched() schedule(); } while(0)
+-#endif
++#endif /* 2.5.70 */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+-#ifndef yield
+-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+-#endif
+-#ifndef minor
+-#define major(d) (MAJOR(to_kdev_t(d)))
+-#define minor(d) (MINOR(to_kdev_t(d)))
+-#endif
+-#ifndef mk_kdev
+-#define mk_kdev(ma,mi) MKDEV(ma,mi)
+-#define kdev_t_to_nr(x)       (x)
+-#endif
++#ifndef container_of
++#define container_of(ptr, type, member) ({                 \
++      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++      (type *)( (char *)__mptr - offsetof(type,member) );})
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+-      /* Is this right? */
+-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0) 
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL)
+-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
++#define kvec iovec
++#define __user 
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+-#define rq_data_dir(x)        ((x)->cmd)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
++#define msleep(x) \
++      set_current_state(TASK_UNINTERRUPTIBLE); \
++      schedule_timeout((x)*(HZ/1000));
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+-
+-#define IS_REQ_CMD(req) (1)
+-
+-#define QUEUE_LOCK(q) (&io_request_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req)) 
+-
+-#else /* > 2.5.0 */
+-
+-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD)
+-
+-#define QUEUE_LOCK(q) ((q)->queue_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock)) 
+-
++#ifndef __iomem
++#define __iomem
+ #endif
+-/* Removed cos it broke stuff. Where is this required anyway? 
+- * #ifndef QUEUE_EMPTY
+- * #define QUEUE_EMPTY  (!CURRENT)
+- * #endif
++#ifndef list_for_each_entry_safe
++/**
++ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
++ * @pos:      the type * to use as a loop counter.
++ * @n:                another type * to use as temporary storage
++ * @head:     the head for your list.
++ * @member:   the name of the list_struct within the struct.
+  */
+-#if LINUX_VERSION_CODE < 0x20300
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
+-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si)
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
+-#else
+-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE))
+-#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
++#define list_for_each_entry_safe(pos, n, head, member)                        \
++      for (pos = list_entry((head)->next, typeof(*pos), member),      \
++              n = list_entry(pos->member.next, typeof(*pos), member); \
++           &pos->member != (head);                                    \
++           pos = n, n = list_entry(n->member.next, typeof(*n), member))
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+-#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+-#define get_seconds() CURRENT_TIME
++#ifndef DEFINE_SPINLOCK
++#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
+ #endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
+-#define generic_file_readonly_mmap generic_file_mmap
++#ifndef DEFINE_RWLOCK
++#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
+ #endif
+ #endif /* __LINUX_MTD_COMPATMAC_H__ */
+--- linux-2.4.21/include/linux/mtd/doc2000.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/doc2000.h
+@@ -1,13 +1,21 @@
+-
+-/* Linux driver for Disk-On-Chip 2000       */
+-/* (c) 1999 Machine Vision Holdings, Inc.   */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/* 
++ * Linux driver for Disk-On-Chip devices
++ *
++ * Copyright (C) 1999 Machine Vision Holdings, Inc.   
++ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
++ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * Copyright (C) 2002-2003 SnapGear Inc
++ *
++ * $Id$ 
++ *
++ * Released under GPL
++ */
+ #ifndef __MTD_DOC2000_H__
+ #define __MTD_DOC2000_H__
+ #include <linux/mtd/mtd.h>
++#include <asm/semaphore.h>
+ #define DoC_Sig1 0
+ #define DoC_Sig2 1
+@@ -38,22 +46,51 @@
+ #define DoC_Mil_CDSN_IO       0x0800
+ #define DoC_2k_CDSN_IO                0x1800
++#define DoC_Mplus_NOP                 0x1002
++#define DoC_Mplus_AliasResolution     0x1004
++#define DoC_Mplus_DOCControl          0x1006
++#define DoC_Mplus_AccessStatus                0x1008
++#define DoC_Mplus_DeviceSelect                0x1008
++#define DoC_Mplus_Configuration               0x100a
++#define DoC_Mplus_OutputControl               0x100c
++#define DoC_Mplus_FlashControl                0x1020
++#define DoC_Mplus_FlashSelect                 0x1022
++#define DoC_Mplus_FlashCmd            0x1024
++#define DoC_Mplus_FlashAddress                0x1026
++#define DoC_Mplus_FlashData0          0x1028
++#define DoC_Mplus_FlashData1          0x1029
++#define DoC_Mplus_ReadPipeInit                0x102a
++#define DoC_Mplus_LastDataRead                0x102c
++#define DoC_Mplus_LastDataRead1               0x102d
++#define DoC_Mplus_WritePipeTerm       0x102e
++#define DoC_Mplus_ECCSyndrome0                0x1040
++#define DoC_Mplus_ECCSyndrome1                0x1041
++#define DoC_Mplus_ECCSyndrome2                0x1042
++#define DoC_Mplus_ECCSyndrome3                0x1043
++#define DoC_Mplus_ECCSyndrome4                0x1044
++#define DoC_Mplus_ECCSyndrome5                0x1045
++#define DoC_Mplus_ECCConf             0x1046
++#define DoC_Mplus_Toggle              0x1046
++#define DoC_Mplus_DownloadStatus      0x1074
++#define DoC_Mplus_CtrlConfirm         0x1076
++#define DoC_Mplus_Power                       0x1fff
++
+ /* How to access the device? 
+  * On ARM, it'll be mmap'd directly with 32-bit wide accesses. 
+  * On PPC, it's mmap'd and 16-bit wide.
+  * Others use readb/writeb 
+  */
+ #if defined(__arm__)
+-#define ReadDOC_(adr, reg)      ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
+-#define WriteDOC_(d, adr, reg)  do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
++#define ReadDOC_(adr, reg)      ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
++#define WriteDOC_(d, adr, reg)  do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x8000
+ #elif defined(__ppc__)
+-#define ReadDOC_(adr, reg)      ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
+-#define WriteDOC_(d, adr, reg)  do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
++#define ReadDOC_(adr, reg)      ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
++#define WriteDOC_(d, adr, reg)  do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x4000
+ #else
+-#define ReadDOC_(adr, reg)      readb(((unsigned long)adr) + (reg))
+-#define WriteDOC_(d, adr, reg)  writeb(d, ((unsigned long)adr) + (reg))
++#define ReadDOC_(adr, reg)      readb((void __iomem *)(adr) + (reg))
++#define WriteDOC_(d, adr, reg)  writeb(d, (void __iomem *)(adr) + (reg))
+ #define DOC_IOREMAP_LEN 0x2000
+ #endif
+@@ -71,13 +108,21 @@
+ #define DOC_MODE_RESERVED1    2
+ #define DOC_MODE_RESERVED2    3
+-#define DOC_MODE_MDWREN       4
+ #define DOC_MODE_CLR_ERR      0x80
++#define       DOC_MODE_RST_LAT        0x10
++#define       DOC_MODE_BDECT          0x08
++#define DOC_MODE_MDWREN       0x04
+ #define DOC_ChipID_Doc2k      0x20
++#define DOC_ChipID_Doc2kTSOP  0x21    /* internal number for MTD */
+ #define DOC_ChipID_DocMil     0x30
++#define DOC_ChipID_DocMilPlus32       0x40
++#define DOC_ChipID_DocMilPlus16       0x41
+ #define CDSN_CTRL_FR_B                0x80
++#define CDSN_CTRL_FR_B0               0x40
++#define CDSN_CTRL_FR_B1               0x80
++
+ #define CDSN_CTRL_ECC_IO      0x20
+ #define CDSN_CTRL_FLASH_IO    0x10
+ #define CDSN_CTRL_WP          0x08
+@@ -93,6 +138,10 @@
+ #define DOC_ECC_RESV          0x02
+ #define DOC_ECC_IGNORE                0x01
++#define DOC_FLASH_CE          0x80
++#define DOC_FLASH_WP          0x40
++#define DOC_FLASH_BANK                0x02
++
+ /* We have to also set the reserved bit 1 for enable */
+ #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
+ #define DOC_ECC_DIS (DOC_ECC_RESV)
+@@ -107,18 +156,21 @@
+ #define MAX_FLOORS 4
+ #define MAX_CHIPS 4
+-#define MAX_FLOORS_MIL 4
++#define MAX_FLOORS_MIL 1
+ #define MAX_CHIPS_MIL 1
++#define MAX_FLOORS_MPLUS 2
++#define MAX_CHIPS_MPLUS 1
++
+ #define ADDR_COLUMN 1
+ #define ADDR_PAGE 2
+ #define ADDR_COLUMN_PAGE 3
+ struct DiskOnChip {
+       unsigned long physadr;
+-      unsigned long virtadr;
++      void __iomem *virtadr;
+       unsigned long totlen;
+-      char ChipID; /* Type of DiskOnChip */
++      unsigned char ChipID; /* Type of DiskOnChip */
+       int ioreg;
+       
+       unsigned long mfr; /* Flash IDs - only one type of flash per device */
+@@ -126,6 +178,7 @@
+       int chipshift;
+       char page256;
+       char pageadrlen;
++      char interleave; /* Internal interleaving - Millennium Plus style */
+       unsigned long erasesize;
+       
+       int curfloor;
+--- linux-2.4.21/include/linux/mtd/flashchip.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/flashchip.h
+@@ -6,7 +6,7 @@
+  *
+  * (C) 2000 Red Hat. GPLd.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+@@ -29,6 +29,7 @@
+       FL_ERASE_SUSPENDED,
+       FL_WRITING,
+       FL_WRITING_TO_BUFFER,
++      FL_OTP_WRITE,
+       FL_WRITE_SUSPENDING,
+       FL_WRITE_SUSPENDED,
+       FL_PM_SUSPENDED,
+@@ -37,13 +38,16 @@
+       FL_LOCKING,
+       FL_UNLOCKING,
+       FL_POINT,
++      FL_XIP_WHILE_ERASING,
++      FL_XIP_WHILE_WRITING,
+       FL_UNKNOWN
+ } flstate_t;
+ /* NOTE: confusingly, this can be used to refer to more than one chip at a time, 
+-   if they're interleaved. */
++   if they're interleaved.  This can even refer to individual partitions on
++   the same physical chip when present. */
+ struct flchip {
+       unsigned long start; /* Offset within the map */
+@@ -58,6 +62,11 @@
+       int ref_point_counter;
+       flstate_t state;
+       flstate_t oldstate;
++
++      int write_suspended:1;
++      int erase_suspended:1;
++      unsigned long in_progress_block_addr;
++
+       spinlock_t *mutex;
+       spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */
+       wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
+@@ -65,8 +74,17 @@
+       int word_write_time;
+       int buffer_write_time;
+       int erase_time;
++
++      void *priv;
+ };
++/* This is used to handle contention on write/erase operations
++   between partitions of the same physical chip. */
++struct flchip_shared {
++      spinlock_t lock;
++      struct flchip *writing;
++      struct flchip *erasing;
++};
+ #endif /* __MTD_FLASHCHIP_H__ */
+--- linux-2.4.21/include/linux/mtd/gen_probe.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/gen_probe.h
+@@ -1,7 +1,7 @@
+ /*
+  * (C) 2001, 2001 Red Hat, Inc.
+  * GPL'd
+- * $Id$
++ * $Id$
+  */
+ #ifndef __LINUX_MTD_GEN_PROBE_H__
+@@ -10,12 +10,12 @@
+ #include <linux/mtd/flashchip.h>
+ #include <linux/mtd/map.h> 
+ #include <linux/mtd/cfi.h>
++#include <linux/bitops.h>
+ struct chip_probe {
+       char *name;
+       int (*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 *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/inftl.h
+@@ -0,0 +1,57 @@
++/*
++ *    inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ *
++ *    (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ *    $Id$
++ */
++
++#ifndef __MTD_INFTL_H__
++#define __MTD_INFTL_H__
++
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include nftl-user.h instead?
++#endif
++
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++
++#include <mtd/inftl-user.h>
++
++#ifndef INFTL_MAJOR
++#define INFTL_MAJOR 94
++#endif
++#define INFTL_PARTN_BITS 4
++
++#ifdef __KERNEL__
++
++struct INFTLrecord {
++      struct mtd_blktrans_dev mbd;
++      __u16 MediaUnit;
++      __u32 EraseSize;
++      struct INFTLMediaHeader MediaHdr;
++      int usecount;
++      unsigned char heads;
++      unsigned char sectors;
++      unsigned short cylinders;
++      __u16 numvunits;
++      __u16 firstEUN;
++      __u16 lastEUN;
++      __u16 numfreeEUNs;
++      __u16 LastFreeEUN;              /* To speed up finding a free EUN */
++      int head,sect,cyl;
++      __u16 *PUtable;                 /* Physical Unit Table  */
++      __u16 *VUtable;                 /* Virtual Unit Table */
++        unsigned int nb_blocks;               /* number of physical blocks */
++        unsigned int nb_boot_blocks;  /* number of blocks used by the bios */
++        struct erase_info instr;
++        struct nand_oobinfo oobinfo;
++};
++
++int INFTL_mount(struct INFTLrecord *s);
++int INFTL_formatblock(struct INFTLrecord *s, int block);
++
++#endif /* __KERNEL__ */
++
++#endif /* __MTD_INFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/jedec.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/jedec.h
+@@ -7,14 +7,13 @@
+  *
+  * See the AMD flash databook for information on how to operate the interface.
+  *
+- * $Id$
++ * $Id$
+  */
+ #ifndef __LINUX_MTD_JEDEC_H__
+ #define __LINUX_MTD_JEDEC_H__
+ #include <linux/types.h>
+-#include <linux/mtd/map.h>
+ #define MAX_JEDEC_CHIPS 16
+--- linux-2.4.21/include/linux/mtd/map.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/map.h
+@@ -1,23 +1,176 @@
+ /* Overhauled routines for dealing with different mmap regions of flash */
+-/* $Id$ */
++/* $Id$ */
+ #ifndef __LINUX_MTD_MAP_H__
+ #define __LINUX_MTD_MAP_H__
+ #include <linux/config.h>
+ #include <linux/types.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/mtd/compatmac.h>
++#include <asm/unaligned.h>
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/bug.h>
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++#define map_bankwidth(map) 1
++#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
++#define map_bankwidth_is_large(map) (0)
++#define map_words(map) (1)
++#define MAX_MAP_BANKWIDTH 1
++#else
++#define map_bankwidth_is_1(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++# else
++#  define map_bankwidth(map) 2
++#  define map_bankwidth_is_large(map) (0)
++#  define map_words(map) (1)
++# endif
++#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 2
++#else
++#define map_bankwidth_is_2(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++# else
++#  define map_bankwidth(map) 4
++#  define map_bankwidth_is_large(map) (0)
++#  define map_words(map) (1)
++# endif
++#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 4
++#else
++#define map_bankwidth_is_4(map) (0)
++#endif
++
++/* ensure we never evaluate anything shorted than an unsigned long
++ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
++
++#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  if BITS_PER_LONG < 64
++#   undef map_bankwidth_is_large
++#   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#   undef map_words
++#   define map_words(map) map_calc_words(map)
++#  endif
++# else
++#  define map_bankwidth(map) 8
++#  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 8
++#else
++#define map_bankwidth_is_8(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  undef map_bankwidth_is_large
++#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#  undef map_words
++#  define map_words(map) map_calc_words(map)
++# else
++#  define map_bankwidth(map) 16
++#  define map_bankwidth_is_large(map) (1)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 16
++#else
++#define map_bankwidth_is_16(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  undef map_bankwidth_is_large
++#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#  undef map_words
++#  define map_words(map) map_calc_words(map)
++# else
++#  define map_bankwidth(map) 32
++#  define map_bankwidth_is_large(map) (1)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 32
++#else
++#define map_bankwidth_is_32(map) (0)
++#endif
++
++#ifndef map_bankwidth
++#error "No bus width supported. What's the point?"
++#endif
++
++static inline int map_bankwidth_supported(int w)
++{
++      switch (w) {
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++      case 1:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++      case 2:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++      case 4:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++      case 8:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++      case 16:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++      case 32:
++#endif
++              return 1;
++
++      default:
++              return 0;
++      }
++}
++
++#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
++
++typedef union {
++      unsigned long x[MAX_MAP_LONGS];
++} map_word;
+ /* The map stuff is very simple. You fill in your struct map_info with
+    a handful of routines for accessing the device, making sure they handle
+    paging etc. correctly if your device needs it. Then you pass it off
+-   to a chip driver which deals with a mapped device - generally either
+-   do_cfi_probe() or do_ram_probe(), either of which will return a 
+-   struct mtd_info if they liked what they saw. At which point, you
+-   fill in the mtd->module with your own module address, and register 
+-   it.
++   to a chip probe routine -- either JEDEC or CFI probe or both -- via
++   do_map_probe(). If a chip is recognised, the probe code will invoke the
++   appropriate chip driver (if present) and return a struct mtd_info.
++   At which point, you fill in the mtd->module with your own module 
++   address, and register it with the MTD core code. Or you could partition
++   it and register the partitions instead, or keep it for your own private
++   use; whatever.
+    
+    The mtd->priv field will point to the struct map_info, and any further
+    private data required by the chip driver is linked from the 
+@@ -29,39 +182,45 @@
+ struct map_info {
+       char *name;
+       unsigned long size;
+-      int buswidth; /* in octets */
+-      __u8 (*read8)(struct map_info *, unsigned long);
+-      __u16 (*read16)(struct map_info *, unsigned long);
+-      __u32 (*read32)(struct map_info *, unsigned long);  
+-      __u64 (*read64)(struct map_info *, unsigned long);  
+-      /* If it returned a 'long' I'd call it readl.
+-       * It doesn't.
+-       * I won't.
+-       * dwmw2 */
++      unsigned long phys;
++#define NO_XIP (-1UL)
+       
++      void __iomem *virt;
++      void *cached;
++
++      int bankwidth; /* in octets. This isn't necessarily the width
++                     of actual bus cycles -- it's the repeat interval
++                    in bytes, before you are talking to the first chip again.
++                    */
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++      map_word (*read)(struct map_info *, unsigned long);
+       void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
+-      void (*write8)(struct map_info *, __u8, unsigned long);
+-      void (*write16)(struct map_info *, __u16, unsigned long);
+-      void (*write32)(struct map_info *, __u32, unsigned long);
+-      void (*write64)(struct map_info *, __u64, unsigned long);
++
++      void (*write)(struct map_info *, const map_word, unsigned long);
+       void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
+-      u_char * (*point) (struct map_info *, loff_t, size_t);
+-      void (*unpoint) (struct map_info *, u_char *, loff_t, size_t);
++      /* We can perhaps put in 'point' and 'unpoint' methods, if we really
++         want to enable XIP for non-linear mappings. Not yet though. */
++#endif
++      /* It's possible for the map driver to use cached memory in its
++         copy_from implementation (and _only_ with copy_from).  However,
++         when the chip driver knows some flash area has changed contents,
++         it will signal it to the map driver through this routine to let
++         the map driver invalidate the corresponding cache as needed.
++         If there is no cache to care about this can be set to NULL. */
++      void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
++      /* set_vpp() must handle being reentered -- enable, enable, disable 
++         must leave it enabled. */
+       void (*set_vpp)(struct map_info *, int);
+-      /* We put these two here rather than a single void *map_priv, 
+-         because we want mappers to be able to have quickly-accessible
+-         cache for the 'currently-mapped page' without the _extra_
+-         redirection that would be necessary. If you need more than
+-         two longs, turn the second into a pointer. dwmw2 */
++
+       unsigned long map_priv_1;
+       unsigned long map_priv_2;
+       void *fldrv_priv;
+       struct mtd_chip_driver *fldrv;
+ };
+-
+ struct mtd_chip_driver {
+       struct mtd_info *(*probe)(struct map_info *map);
+       void (*destroy)(struct mtd_info *);
+@@ -74,26 +233,193 @@
+ void unregister_mtd_chip_driver(struct mtd_chip_driver *);
+ struct mtd_info *do_map_probe(const char *name, struct map_info *map);
++void map_destroy(struct mtd_info *mtd);
++
++#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
++#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++#define INVALIDATE_CACHED_RANGE(map, from, size) \
++      do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
+-/*
+- * Destroy an MTD device which was created for a map device.
+- * Make sure the MTD device is already unregistered before calling this
+- */
+-static inline void map_destroy(struct mtd_info *mtd)
++
++static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
+ {
+-      struct map_info *map = mtd->priv;
++      int i;
++      for (i=0; i<map_words(map); i++) {
++              if (val1.x[i] != val2.x[i])
++                      return 0;
++      }
++      return 1;
++}
+-      if (map->fldrv->destroy)
+-              map->fldrv->destroy(mtd);
+-#ifdef CONFIG_MODULES
+-      if (map->fldrv->module)
+-              __MOD_DEC_USE_COUNT(map->fldrv->module);
++static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
++{
++      map_word r;
++      int i;
++
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] & val2.x[i];
++      }
++      return r;
++}
++
++static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
++{
++      map_word r;
++      int i;
++
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] & ~val2.x[i];
++      }
++      return r;
++}
++
++static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
++{
++      map_word r;
++      int i;
++
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] | val2.x[i];
++      }
++      return r;
++}
++
++#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
++
++static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
++{
++      int i;
++
++      for (i=0; i<map_words(map); i++) {
++              if (val1.x[i] & val2.x[i])
++                      return 1;
++      }
++      return 0;
++}
++
++static inline map_word map_word_load(struct map_info *map, const void *ptr)
++{
++      map_word r;
++
++      if (map_bankwidth_is_1(map))
++              r.x[0] = *(unsigned char *)ptr;
++      else if (map_bankwidth_is_2(map))
++              r.x[0] = get_unaligned((uint16_t *)ptr);
++      else if (map_bankwidth_is_4(map))
++              r.x[0] = get_unaligned((uint32_t *)ptr);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              r.x[0] = get_unaligned((uint64_t *)ptr);
+ #endif
+-      kfree(mtd);
++      else if (map_bankwidth_is_large(map))
++              memcpy(r.x, ptr, map->bankwidth);
++
++      return r;
+ }
+-#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
+-#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++static inline map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
++{
++      int i;
++
++      if (map_bankwidth_is_large(map)) {
++              char *dest = (char *)&orig;
++              memcpy(dest+start, buf, len);
++      } else {
++              for (i=start; i < start+len; i++) {
++                      int bitpos;
++#ifdef __LITTLE_ENDIAN
++                      bitpos = i*8;
++#else /* __BIG_ENDIAN */
++                      bitpos = (map_bankwidth(map)-1-i)*8;
++#endif
++                      orig.x[0] &= ~(0xff << bitpos);
++                      orig.x[0] |= buf[i-start] << bitpos;
++              }
++      }
++      return orig;
++}
++
++static inline map_word map_word_ff(struct map_info *map)
++{
++      map_word r;
++      int i;
++
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = ~0UL;
++      }
++      return r;
++}
++
++static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
++{
++      map_word r;
++
++      if (map_bankwidth_is_1(map))
++              r.x[0] = __raw_readb(map->virt + ofs);
++      else if (map_bankwidth_is_2(map))
++              r.x[0] = __raw_readw(map->virt + ofs);
++      else if (map_bankwidth_is_4(map))
++              r.x[0] = __raw_readl(map->virt + ofs);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              r.x[0] = __raw_readq(map->virt + ofs);
++#endif
++      else if (map_bankwidth_is_large(map))
++              memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
++
++      return r;
++}
++
++static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
++{
++      if (map_bankwidth_is_1(map))
++              __raw_writeb(datum.x[0], map->virt + ofs);
++      else if (map_bankwidth_is_2(map))
++              __raw_writew(datum.x[0], map->virt + ofs);
++      else if (map_bankwidth_is_4(map))
++              __raw_writel(datum.x[0], map->virt + ofs);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              __raw_writeq(datum.x[0], map->virt + ofs);
++#endif
++      else if (map_bankwidth_is_large(map))
++              memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
++      mb();
++}
++
++static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++      if (map->cached)
++              memcpy(to, (char *)map->cached + from, len);
++      else
++              memcpy_fromio(to, map->virt + from, len);
++}
++
++static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++      memcpy_toio(map->virt + to, from, len);
++}
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++#define map_read(map, ofs) (map)->read(map, ofs)
++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
++
++extern void simple_map_init(struct map_info *);
++#define map_is_linear(map) (map->phys != NO_XIP)
++
++#else
++#define map_read(map, ofs) inline_map_read(map, ofs)
++#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
++
++
++#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
++#define map_is_linear(map) ({ (void)(map); 1; })
++
++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
+ #endif /* __LINUX_MTD_MAP_H__ */
+--- linux-2.4.21/include/linux/mtd/mtd.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/mtd.h
+@@ -1,123 +1,45 @@
+-
+-/* $Id$ */
++/* 
++ * $Id$
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
+ #ifndef __MTD_MTD_H__
+ #define __MTD_MTD_H__
+-#ifdef __KERNEL__
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include mtd-user.h instead?
++#endif
+ #include <linux/config.h>
+ #include <linux/version.h>
+ #include <linux/types.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/module.h>
+ #include <linux/uio.h>
+-#endif /* __KERNEL__ */
+-
+-struct erase_info_user {
+-      u_int32_t start;
+-      u_int32_t length;
+-};
+-
+-struct mtd_oob_buf {
+-      u_int32_t start;
+-      u_int32_t length;
+-      unsigned char *ptr;
+-};
+-
++#include <linux/mtd/compatmac.h>
++#include <mtd/mtd-abi.h>
+ #define MTD_CHAR_MAJOR 90
+ #define MTD_BLOCK_MAJOR 31
+ #define MAX_MTD_DEVICES 16
+-
+-
+-#define MTD_ABSENT            0
+-#define MTD_RAM                       1
+-#define MTD_ROM                       2
+-#define MTD_NORFLASH          3
+-#define MTD_NANDFLASH         4
+-#define MTD_PEROM             5
+-#define MTD_OTHER             14
+-#define MTD_UNKNOWN           15
+-
+-
+-
+-#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
+-#define MTD_SET_BITS          2       // Bits can be set
+-#define MTD_ERASEABLE         4       // Has an erase function
+-#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
+-#define MTD_VOLATILE          16      // Set for RAMs
+-#define MTD_XIP                       32      // eXecute-In-Place possible
+-#define MTD_OOB                       64      // Out-of-band data (NAND flash)
+-#define MTD_ECC                       128     // Device capable of automatic ECC
+-
+-// Some common devices / combinations of capabilities
+-#define MTD_CAP_ROM           0
+-#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+-#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
+-#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+-#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
+-
+-
+-// Types of automatic ECC/Checksum available
+-#define MTD_ECC_NONE          0       // No automatic ECC available
+-#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
+-#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
+-
+-struct mtd_info_user {
+-      u_char type;
+-      u_int32_t flags;
+-      u_int32_t size;  // Total size of the MTD
+-      u_int32_t erasesize;
+-      u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+-      u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+-      u_int32_t ecctype;
+-      u_int32_t eccsize;
+-};
+-
+-struct region_info_user {
+-      u_int32_t offset;               /* At which this region starts, 
+-                                       * from the beginning of the MTD */
+-      u_int32_t erasesize;            /* For this region */
+-      u_int32_t numblocks;            /* Number of blocks in this region */
+-      u_int32_t regionindex;
+-};
+-
+-#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
+-#define MEMERASE                _IOW('M', 2, struct erase_info_user)
+-#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
+-#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
+-#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
+-#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
+-#define MEMGETREGIONCOUNT     _IOR('M', 7, int)
+-#define MEMGETREGIONINFO      _IOWR('M', 8, struct region_info_user)
+-#define       MEMREADDATA             _IOWR('M', 9, struct mtd_oob_buf)
+-#define       MEMWRITEDATA            _IOWR('M', 10, struct mtd_oob_buf)
+-
+-#ifndef __KERNEL__
+-
+-typedef struct mtd_info_user mtd_info_t;
+-typedef struct erase_info_user erase_info_t;
+-typedef struct region_info_user region_info_t;
+-
+-      /* User-space ioctl definitions */
+-
+-
+-#else /* __KERNEL__ */
+-
+-
+ #define MTD_ERASE_PENDING             0x01
+ #define MTD_ERASING           0x02
+ #define MTD_ERASE_SUSPEND     0x04
+ #define MTD_ERASE_DONE          0x08
+ #define MTD_ERASE_FAILED        0x10
++/* If the erase fails, fail_addr might indicate exactly which block failed.  If
++   fail_addr = 0xffffffff, the failure was not at the device level or was not
++   specific to any particular block. */
+ struct erase_info {
+       struct mtd_info *mtd;
+       u_int32_t addr;
+       u_int32_t len;
++      u_int32_t fail_addr;
+       u_long time;
+       u_long retries;
+       u_int dev;
+@@ -147,13 +69,18 @@
+       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
++      u_int32_t oobavail;  // Number of bytes in OOB area available for fs 
+       u_int32_t ecctype;
+       u_int32_t eccsize;
++
+       // Kernel-only stuff starts here.
+       char *name;
+       int index;
++      // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
++      struct nand_oobinfo oobinfo;
++
+       /* Data for variable erase regions. If numeraseregions is zero,
+        * it means that the whole device has erasesize as given above. 
+        */
+@@ -163,7 +90,6 @@
+       /* This really shouldn't be here. It can go away in 2.5 */
+       u_int32_t bank_size;
+-      struct module *module;
+       int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
+       /* This stuff for eXecute-In-Place */
+@@ -176,8 +102,8 @@
+       int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+-      int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+-      int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
++      int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
++      int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
+       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+@@ -187,24 +113,24 @@
+        * flash devices. The user data is one time programmable but the
+        * factory data is read only. 
+        */
+-      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
++      int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
+-      /* This function is not yet implemented */
++      int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
++      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++      int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
+-      /* iovec-based read/write methods. We need these especially for NAND flash,
++      /* kvec-based read/write methods. We need these especially for NAND flash,
+          with its limited number of write cycles per erase.
+          NB: The 'count' parameter is the number of _vectors_, each of 
+          which contains an (ofs, len) tuple.
+       */
+-      int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
+-      int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, 
+-              size_t *retlen, u_char *eccbuf, int oobsel);
+-      int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+-      int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, 
+-              size_t *retlen, u_char *eccbuf, int oobsel);
++      int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
++      int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, 
++              size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
++      int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
++      int (*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);
+       /* Sync */
+       void (*sync) (struct mtd_info *mtd);
+@@ -217,7 +143,14 @@
+       int (*suspend) (struct mtd_info *mtd);
+       void (*resume) (struct mtd_info *mtd);
++      /* Bad block management functions */
++      int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
++      int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
++
+       void *priv;
++
++      struct module *owner;
++      int usecount;
+ };
+@@ -226,44 +159,27 @@
+ extern int add_mtd_device(struct mtd_info *mtd);
+ extern int del_mtd_device (struct mtd_info *mtd);
+-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
+-
+-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+-{
+-      struct mtd_info *ret;
+-      
+-      ret = __get_mtd_device(mtd, num);
+-
+-      if (ret && ret->module && !try_inc_mod_count(ret->module))
+-              return NULL;
+-
+-      return ret;
+-}
++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+-static inline void put_mtd_device(struct mtd_info *mtd)
+-{
+-       if (mtd->module)
+-             __MOD_DEC_USE_COUNT(mtd->module);
+-}
++extern void put_mtd_device(struct mtd_info *mtd);
+ struct mtd_notifier {
+       void (*add)(struct mtd_info *mtd);
+       void (*remove)(struct mtd_info *mtd);
+-      struct mtd_notifier *next;
++      struct list_head list;
+ };
+ extern void register_mtd_user (struct mtd_notifier *new);
+ extern int unregister_mtd_user (struct mtd_notifier *old);
+-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
++int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+                      unsigned long count, loff_t to, size_t *retlen);
+-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);
+-#ifndef MTDC
+ #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
+ #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
+ #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
+@@ -276,7 +192,17 @@
+ #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
+ #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
+ #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
+-#endif /* MTDC */
++
++
++#ifdef CONFIG_MTD_PARTITIONS
++void mtd_erase_callback(struct erase_info *instr);
++#else
++static inline void mtd_erase_callback(struct erase_info *instr)
++{
++      if (instr->callback)
++              instr->callback(instr);
++}
++#endif
+ /*
+  * Debugging macro and defines
+@@ -288,13 +214,13 @@
+ #ifdef CONFIG_MTD_DEBUG
+ #define DEBUG(n, args...)                     \
+-      if (n <=  CONFIG_MTD_DEBUG_VERBOSE) {   \
++      do {                                            \
++              if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
+               printk(KERN_INFO args); \
+-      }
++      } while(0)
+ #else /* CONFIG_MTD_DEBUG */
+-#define DEBUG(n, args...)
+-#endif /* CONFIG_MTD_DEBUG */
++#define DEBUG(n, args...) do { } while(0)
+-#endif /* __KERNEL__ */
++#endif /* CONFIG_MTD_DEBUG */
+ #endif /* __MTD_MTD_H__ */
+--- linux-2.4.21/include/linux/mtd/nand.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand.h
+@@ -2,10 +2,10 @@
+  *  linux/include/linux/mtd/nand.h
+  *
+  *  Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+- *                     Steven J. Hill <sjhill@cotw.com>
++ *                     Steven J. Hill <sjhill@realitydiluted.com>
+  *                   Thomas Gleixner <tglx@linutronix.de>
+  *
+- * $Id$
++ * $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
+@@ -44,27 +44,61 @@
+  *                    NAND_YAFFS_OOB
+  *  11-25-2002 tglx   Added Manufacturer code FUJITSU, NATIONAL
+  *                    Split manufacturer and device ID structures 
++ *
++ *  02-08-2004 tglx   added option field to nand structure for chip anomalities
++ *  05-25-2004 tglx   added bad block table support, ST-MICRO manufacturer id
++ *                    update of nand_chip structure description
++ *  01-17-2005 dmarlin        added extended commands for AG-AND device and added option 
++ *                    for BBT_AUTO_REFRESH.
++ *  01-20-2005 dmarlin        added optional pointer to hardware specific callback for 
++ *                    extra error status checks.
+  */
+ #ifndef __LINUX_MTD_NAND_H
+ #define __LINUX_MTD_NAND_H
+ #include <linux/config.h>
+-#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/spinlock.h>
++#include <linux/mtd/mtd.h>
+-/*
+- * Searches for a NAND device
++struct mtd_info;
++/* Scan and identify a NAND device */
++extern int nand_scan (struct mtd_info *mtd, int max_chips);
++/* Free resources held by the NAND device */
++extern void nand_release (struct mtd_info *mtd);
++
++/* Read raw data from the device without ECC */
++extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
++
++
++/* The maximum number of NAND chips in an array */
++#define NAND_MAX_CHIPS                8
++
++/* This constant declares the max. oobsize / page, which
++ * is supported now. If you add a chip with bigger oobsize/page
++ * adjust this accordingly.
+  */
+-extern int nand_scan (struct mtd_info *mtd);
++#define NAND_MAX_OOBSIZE      64
+ /*
+  * Constants for hardware specific CLE/ALE/NCE function
+ */
++/* 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
+ /*
+  * Standard NAND flash commands
+@@ -75,35 +109,132 @@
+ #define NAND_CMD_READOOB      0x50
+ #define NAND_CMD_ERASE1               0x60
+ #define NAND_CMD_STATUS               0x70
++#define NAND_CMD_STATUS_MULTI 0x71
+ #define NAND_CMD_SEQIN                0x80
+ #define NAND_CMD_READID               0x90
+ #define NAND_CMD_ERASE2               0xd0
+ #define NAND_CMD_RESET                0xff
++/* Extended commands for large page devices */
++#define NAND_CMD_READSTART    0x30
++#define NAND_CMD_CACHEDPROG   0x15
++
++/* Extended commands for AG-AND device */
++/* 
++ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but 
++ *       there is no way to distinguish that from NAND_CMD_READ0
++ *       until the remaining sequence of commands has been completed
++ *       so add a high order bit and mask it off in the command.
++ */
++#define NAND_CMD_DEPLETE1     0x100
++#define NAND_CMD_DEPLETE2     0x38
++#define NAND_CMD_STATUS_MULTI 0x71
++#define NAND_CMD_STATUS_ERROR 0x72
++/* multi-bank error status (banks 0-3) */
++#define NAND_CMD_STATUS_ERROR0        0x73
++#define NAND_CMD_STATUS_ERROR1        0x74
++#define NAND_CMD_STATUS_ERROR2        0x75
++#define NAND_CMD_STATUS_ERROR3        0x76
++#define NAND_CMD_STATUS_RESET 0x7f
++#define NAND_CMD_STATUS_CLEAR 0xff
++
++/* Status bits */
++#define NAND_STATUS_FAIL      0x01
++#define NAND_STATUS_FAIL_N1   0x02
++#define NAND_STATUS_TRUE_READY        0x20
++#define NAND_STATUS_READY     0x40
++#define NAND_STATUS_WP                0x80
++
+ /* 
+  * Constants for ECC_MODES
+- *
+- * NONE:      No ECC
+- * SOFT:      Software ECC 3 byte ECC per 256 Byte data
+- * HW3_256:   Hardware ECC 3 byte ECC per 256 Byte data
+- * HW3_512:   Hardware ECC 3 byte ECC per 512 Byte data
+- *
+- *
+-*/
++ */
++
++/* 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 3 byte ECC per 512 Byte data */
+ #define NAND_ECC_HW6_512      4
+-#define NAND_ECC_DISKONCHIP   5
++/* Hardware ECC 8 byte ECC per 512 Byte data */
++#define NAND_ECC_HW8_512      6
++/* Hardware ECC 12 byte ECC per 2048 Byte data */
++#define NAND_ECC_HW12_2048    7
+ /*
+  * Constants for Hardware ECC
+-*/
++ */
++/* Reset Hardware ECC for read */
+ #define NAND_ECC_READ         0
++/* Reset Hardware ECC for write */
+ #define NAND_ECC_WRITE                1
++/* Enable Hardware ECC before syndrom is read back from flash */
++#define NAND_ECC_READSYN      2
++
++/* Bit mask for flags passed to do_nand_read_ecc */
++#define NAND_GET_DEVICE               0x80
++
++
++/* Option constants for bizarre disfunctionality and real
++*  features
++*/
++/* 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 
++/* Chip requires that BBT is periodically rewritten to prevent
++ * bits from adjacent blocks from 'leaking' in altering data.
++ * This happens with the Renesas AG-AND chips, possibly others.  */
++#define BBT_AUTO_REFRESH      0x00000080
++
++/* Options valid for Samsung large page devices */
++#define NAND_SAMSUNG_LP_OPTIONS \
++      (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
++
++/* Macros to identify the above */
++#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
++#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
++#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
++#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
++
++/* Mask to zero out the chip options, which come from the id table */
++#define NAND_CHIPOPTIONS_MSK  (0x0000ffff & ~NAND_NO_AUTOINCR)
++
++/* Non chip related options */
++/* Use a flash based bad block table. This option is passed to the
++ * default bad block table function. */
++#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
++/* This option skips the bbt scan during initialization. */
++#define NAND_SKIP_BBTSCAN     0x00040000
++
++/* Options set by nand scan */
++/* Nand scan has allocated oob_buf */
++#define NAND_OOBBUF_ALLOC     0x40000000
++/* Nand scan has allocated data_buf */
++#define NAND_DATABUF_ALLOC    0x80000000
++
+       
+ /*
++ * nand_state_t - chip states
+  * Enumeration for NAND flash chip state
+  */
+ typedef enum {
+@@ -111,73 +242,137 @@
+       FL_READING,
+       FL_WRITING,
+       FL_ERASING,
+-      FL_SYNCING
++      FL_SYNCING,
++      FL_CACHEDPRG,
+ } nand_state_t;
++/* Keep gcc happy */
++struct nand_chip;
+-/*
+- * NAND Private Flash Chip Data
+- *
+- * Structure overview:
+- *
+- *  IO_ADDR_R - address to read the 8 I/O lines of the flash device 
+- *
+- *  IO_ADDR_W - address to write the 8 I/O lines of the flash device 
+- *
+- *  hwcontrol - hardwarespecific function for accesing control-lines
+- *
+- *  dev_ready - hardwarespecific function for accesing device ready/busy line
+- *
+- *  waitfunc - hardwarespecific function for wait on ready
+- *
+- *  calculate_ecc - function for ecc calculation or readback from ecc hardware
+- *
+- *  correct_data - function for ecc correction, matching to ecc generator (sw/hw)
+- *
+- *  enable_hwecc - function to enable (reset) hardware ecc generator
+- *
+- *  eccmod - mode of ecc: see constants
+- *
+- *  eccsize - databytes used per ecc-calculation
+- *
+- *  chip_delay - chip dependent delay for transfering data from array to read regs (tR)
+- *
+- *  chip_lock - spinlock used to protect access to this structure
+- *
+- *  wq - wait queue to sleep on if a NAND operation is in progress
+- *
+- *  state - give the current state of the NAND device
+- *
+- *  page_shift - number of address bits in a page (column address bits)
+- *
+- *  data_buf - data buffer passed to/from MTD user modules
+- *
+- *  data_cache - data cache for redundant page access and shadow for
+- *             ECC failure
+- *
+- *  cache_page - number of last valid page in page_cache 
++/**
++ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
++ * @lock:               protection lock  
++ * @active:           the mtd device which holds the controller currently
+  */
++struct nand_hw_control {
++      spinlock_t       lock;
++      struct nand_chip *active;
++};
++
++/**
++ * struct nand_chip - NAND Private Flash Chip Data
++ * @IO_ADDR_R:                [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device 
++ * @IO_ADDR_W:                [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device 
++ * @read_byte:                [REPLACEABLE] read one byte from the chip
++ * @write_byte:               [REPLACEABLE] write one byte to the chip
++ * @read_word:                [REPLACEABLE] read one word from the chip
++ * @write_word:               [REPLACEABLE] write one word to the chip
++ * @write_buf:                [REPLACEABLE] write data from the buffer to the chip
++ * @read_buf:         [REPLACEABLE] read data from the chip into the buffer
++ * @verify_buf:               [REPLACEABLE] verify buffer contents against the chip data
++ * @select_chip:      [REPLACEABLE] select chip nr
++ * @block_bad:                [REPLACEABLE] check, if the block is bad
++ * @block_markbad:    [REPLACEABLE] mark the block bad
++ * @hwcontrol:                [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
++ * @dev_ready:                [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
++ *                    If set to NULL no access to ready/busy is available and the ready/busy information
++ *                    is read from the chip status register
++ * @cmdfunc:          [REPLACEABLE] hardwarespecific function for writing commands to the chip
++ * @waitfunc:         [REPLACEABLE] hardwarespecific function for wait on ready
++ * @calculate_ecc:    [REPLACEABLE] function for ecc calculation or readback from ecc hardware
++ * @correct_data:     [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
++ * @enable_hwecc:     [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
++ *                    be provided if a hardware ECC is available
++ * @erase_cmd:                [INTERN] erase command write function, selectable due to AND support
++ * @scan_bbt:         [REPLACEABLE] function to scan bad block table
++ * @eccmode:          [BOARDSPECIFIC] mode of ecc, see defines 
++ * @eccsize:          [INTERN] databytes used per ecc-calculation
++ * @eccbytes:                 [INTERN] number of ecc bytes per ecc-calculation step
++ * @eccsteps:         [INTERN] number of ecc calculation steps per page
++ * @chip_delay:               [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
++ * @chip_lock:                [INTERN] spinlock used to protect access to this structure and the chip
++ * @wq:                       [INTERN] wait queue to sleep on if a NAND operation is in progress
++ * @state:            [INTERN] the current state of the NAND device
++ * @page_shift:               [INTERN] number of address bits in a page (column address bits)
++ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
++ * @bbt_erase_shift:  [INTERN] number of address bits in a bbt entry
++ * @chip_shift:               [INTERN] number of address bits in one chip
++ * @data_buf:         [INTERN] internal buffer for one page + oob 
++ * @oob_buf:          [INTERN] oob buffer for one eraseblock
++ * @oobdirty:         [INTERN] indicates that oob_buf must be reinitialized
++ * @data_poi:         [INTERN] pointer to a data buffer
++ * @options:          [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
++ *                    special functionality. See the defines for further explanation
++ * @badblockpos:      [INTERN] position of the bad block marker in the oob area
++ * @numchips:         [INTERN] number of physical chips
++ * @chipsize:         [INTERN] the size of one chip for multichip arrays
++ * @pagemask:         [INTERN] page number mask = number of (pages / chip) - 1
++ * @pagebuf:          [INTERN] holds the pagenumber which is currently in data_buf
++ * @autooob:          [REPLACEABLE] the default (auto)placement scheme
++ * @bbt:              [INTERN] bad block table pointer
++ * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup
++ * @bbt_md:           [REPLACEABLE] bad block table mirror descriptor
++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan 
++ * @controller:               [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
++ * @priv:             [OPTIONAL] pointer to private chip date
++ * @errstat:          [OPTIONAL] hardware specific function to perform additional error status checks 
++ *                    (determine if errors are correctable)
++ */
++ 
+ struct nand_chip {
+-      unsigned long   IO_ADDR_R;
+-      unsigned long   IO_ADDR_W;
+-      void            (*hwcontrol)(int cmd);
+-      int             (*dev_ready)(void);
++      void  __iomem   *IO_ADDR_R;
++      void  __iomem   *IO_ADDR_W;
++      
++      u_char          (*read_byte)(struct mtd_info *mtd);
++      void            (*write_byte)(struct mtd_info *mtd, u_char byte);
++      u16             (*read_word)(struct mtd_info *mtd);
++      void            (*write_word)(struct mtd_info *mtd, u16 word);
++      
++      void            (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++      void            (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
++      int             (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++      void            (*select_chip)(struct mtd_info *mtd, int chip);
++      int             (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
++      int             (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
++      void            (*hwcontrol)(struct mtd_info *mtd, int cmd);
++      int             (*dev_ready)(struct mtd_info *mtd);
+       void            (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
+       int             (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
+-      void            (*calculate_ecc)(const u_char *dat, u_char *ecc_code);
+-      int             (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+-      void            (*enable_hwecc)(int mode);
++      int             (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
++      int             (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++      void            (*enable_hwecc)(struct mtd_info *mtd, int mode);
++      void            (*erase_cmd)(struct mtd_info *mtd, int page);
++      int             (*scan_bbt)(struct mtd_info *mtd);
+       int             eccmode;
+       int             eccsize;
++      int             eccbytes;
++      int             eccsteps;
+       int             chip_delay;
+       spinlock_t      chip_lock;
+       wait_queue_head_t wq;
+       nand_state_t    state;
+       int             page_shift;
++      int             phys_erase_shift;
++      int             bbt_erase_shift;
++      int             chip_shift;
+       u_char          *data_buf;
++      u_char          *oob_buf;
++      int             oobdirty;
+       u_char          *data_poi;
+-      u_char          *data_cache;
+-      int             cache_page;
++      unsigned int    options;
++      int             badblockpos;
++      int             numchips;
++      unsigned long   chipsize;
++      int             pagemask;
++      int             pagebuf;
++      struct nand_oobinfo     *autooob;
++      uint8_t         *bbt;
++      struct nand_bbt_descr   *bbt_td;
++      struct nand_bbt_descr   *bbt_md;
++      struct nand_bbt_descr   *badblock_pattern;
++      struct nand_hw_control  *controller;
++      void            *priv;
++      int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
+ };
+ /*
+@@ -187,46 +382,35 @@
+ #define NAND_MFR_SAMSUNG      0xec
+ #define NAND_MFR_FUJITSU      0x04
+ #define NAND_MFR_NATIONAL     0x8f
++#define NAND_MFR_RENESAS      0x07
++#define NAND_MFR_STMICRO      0x20
+-/*
+- * NAND Flash Device ID Structure
+- *
+- * Structure overview:
+- *
+- *  name - Identify the device type
+- *
+- *  id -  device ID code
+- *
+- *  chipshift - total number of address bits for the device which
+- *              is used to calculate address offsets and the total
+- *              number of bytes the device is capable of.
+- *
+- *  page256 - denotes if flash device has 256 byte pages or not.
+- *
+- *  pageadrlen - number of bytes minus one needed to hold the
+- *               complete address into the flash array. Keep in
+- *               mind that when a read or write is done to a
+- *               specific address, the address is input serially
+- *               8 bits at a time. This structure member is used
+- *               by the read/write routines as a loop index for
+- *               shifting the address out 8 bits at a time.
++/**
++ * struct nand_flash_dev - NAND Flash Device ID Structure
+  *
+- *  erasesize - size of an erase block in the flash device.
++ * @name:     Identify the device type
++ * @id:       device ID code
++ * @pagesize:         Pagesize in bytes. Either 256 or 512 or 0
++ *            If the pagesize is 0, then the real pagesize 
++ *            and the eraseize are determined from the
++ *            extended id bytes in the chip
++ * @erasesize:        Size of an erase block in the flash device.
++ * @chipsize:         Total chipsize in Mega Bytes
++ * @options:  Bitfield to store chip relevant options
+  */
+ struct nand_flash_dev {
+-      char * name;
++      char *name;
+       int id;
+-      int chipshift;
++      unsigned long pagesize;
++      unsigned long chipsize;
+       unsigned long erasesize;
+-      char page256;
++      unsigned long options;
+ };
+-/*
+- * NAND Flash Manufacturer ID Structure
+- *
+- *  name - Manufacturer name
+- *
+- *  id - manufacturer ID code of device.
++/**
++ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
++ * @name:     Manufacturer name
++ * @id:       manufacturer ID code of device.
+ */
+ struct nand_manufacturers {
+       int id;
+@@ -236,39 +420,88 @@
+ extern struct nand_flash_dev nand_flash_ids[];
+ extern struct nand_manufacturers nand_manuf_ids[];
+-/*
+-* Constants for oob configuration
+-*/
+-#define NAND_BADBLOCK_POS             5
++/** 
++ * struct nand_bbt_descr - bad block table descriptor
++ * @options:  options for this descriptor
++ * @pages:    the page(s) where we find the bbt, used with option BBT_ABSPAGE
++ *            when bbt is searched, then we store the found bbts pages here.
++ *            Its an array and supports up to 8 chips now
++ * @offs:     offset of the pattern in the oob area of the page
++ * @veroffs:  offset of the bbt version counter in the oob are of the page
++ * @version:  version read from the bbt page during scan
++ * @len:      length of the pattern, if 0 no pattern check is performed
++ * @maxblocks:        maximum number of blocks to search for a bbt. This number of
++ *            blocks is reserved at the end of the device where the tables are 
++ *            written.
++ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
++ *              bad) block in the stored bbt
++ * @pattern:  pattern to identify bad block table or factory marked good / 
++ *            bad blocks, can be NULL, if len = 0
++ *
++ * Descriptor for the bad block table marker and the descriptor for the 
++ * pattern which identifies good and bad blocks. The assumption is made
++ * that the pattern and the version count are always located in the oob area
++ * of the first block.
++ */
++struct nand_bbt_descr {
++      int     options;
++      int     pages[NAND_MAX_CHIPS];
++      int     offs;
++      int     veroffs;
++      uint8_t version[NAND_MAX_CHIPS];
++      int     len;
++      int     maxblocks;
++      int     reserved_block_code;
++      uint8_t *pattern;
++};
+-#define NAND_NONE_OOB                 0
+-#define NAND_JFFS2_OOB                        1
+-#define NAND_YAFFS_OOB                        2
++/* Options for the bad block table descriptors */
+-#define NAND_NOOB_ECCPOS0             0
+-#define NAND_NOOB_ECCPOS1             1
+-#define NAND_NOOB_ECCPOS2             2
+-#define NAND_NOOB_ECCPOS3             3
+-#define NAND_NOOB_ECCPOS4             6
+-#define NAND_NOOB_ECCPOS5             7
++/* 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
++/* Search good / bad pattern on the first and the second page */
++#define NAND_BBT_SCAN2NDPAGE  0x00004000
+-#define NAND_JFFS2_OOB_ECCPOS0                0
+-#define NAND_JFFS2_OOB_ECCPOS1                1
+-#define NAND_JFFS2_OOB_ECCPOS2                2
+-#define NAND_JFFS2_OOB_ECCPOS3                3
+-#define NAND_JFFS2_OOB_ECCPOS4                6
+-#define NAND_JFFS2_OOB_ECCPOS5                7
++/* The maximum number of blocks to scan for a bbt */
++#define NAND_BBT_SCAN_MAXBLOCKS       4
+-#define NAND_YAFFS_OOB_ECCPOS0                8
+-#define NAND_YAFFS_OOB_ECCPOS1                9
+-#define NAND_YAFFS_OOB_ECCPOS2                10
+-#define NAND_YAFFS_OOB_ECCPOS3                13
+-#define NAND_YAFFS_OOB_ECCPOS4                14
+-#define NAND_YAFFS_OOB_ECCPOS5                15
++extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
++extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
++extern int nand_default_bbt (struct mtd_info *mtd);
++extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
++extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
++extern 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);
+-#define NAND_JFFS2_OOB8_FSDAPOS               6
+-#define NAND_JFFS2_OOB16_FSDAPOS      8
+-#define NAND_JFFS2_OOB8_FSDALEN               2
+-#define NAND_JFFS2_OOB16_FSDALEN      8
++/*
++* Constants for oob configuration
++*/
++#define NAND_SMALL_BADBLOCK_POS               5
++#define NAND_LARGE_BADBLOCK_POS               0
+ #endif /* __LINUX_MTD_NAND_H */
+--- linux-2.4.21/include/linux/mtd/nand_ecc.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand_ecc.h
+@@ -1,9 +1,9 @@
+ /*
+  *  drivers/mtd/nand_ecc.h
+  *
+- *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+  *
+- * $Id$
++ * $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
+@@ -12,17 +12,19 @@
+  * This file is the header for the ECC algorithm.
+  */
+-/*
+- * Creates non-inverted ECC code from line parity
+- */
+-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code);
++#ifndef __MTD_NAND_ECC_H__
++#define __MTD_NAND_ECC_H__
++
++struct mtd_info;
+ /*
+  * Calculate 3 byte ECC code for 256 byte block
+  */
+-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);
+ /*
+  * 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);
++
++#endif /* __MTD_NAND_ECC_H__ */
+--- linux-2.4.21/include/linux/mtd/nftl.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nftl.h
+@@ -1,81 +1,16 @@
+-
+-/* Defines for NAND Flash Translation Layer  */
+-/* (c) 1999 Machine Vision Holdings, Inc.    */
+-/* Author: David Woodhouse <dwmw2@mvhi.com>  */
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
++ */
+ #ifndef __MTD_NFTL_H__
+ #define __MTD_NFTL_H__
+-#ifndef __BOOT__
+ #include <linux/mtd/mtd.h>
+-#endif
+-
+-/* Block Control Information */
+-
+-struct nftl_bci {
+-      unsigned char ECCSig[6];
+-      __u8 Status;
+-      __u8 Status1;
+-}__attribute__((packed));
+-
+-/* Unit Control Information */
+-
+-struct nftl_uci0 {
+-      __u16 VirtUnitNum;
+-      __u16 ReplUnitNum;
+-      __u16 SpareVirtUnitNum;
+-      __u16 SpareReplUnitNum;
+-} __attribute__((packed));
+-
+-struct nftl_uci1 {
+-      __u32 WearInfo;
+-      __u16 EraseMark;
+-      __u16 EraseMark1;
+-} __attribute__((packed));
+-
+-struct nftl_uci2 {
+-        __u16 FoldMark;
+-        __u16 FoldMark1;
+-      __u32 unused;
+-} __attribute__((packed));
+-
+-union nftl_uci {
+-      struct nftl_uci0 a;
+-      struct nftl_uci1 b;
+-      struct nftl_uci2 c;
+-};
+-
+-struct nftl_oob {
+-      struct nftl_bci b;
+-      union nftl_uci u;
+-};
+-
+-/* NFTL Media Header */
+-
+-struct NFTLMediaHeader {
+-      char DataOrgID[6];
+-      __u16 NumEraseUnits;
+-      __u16 FirstPhysicalEUN;
+-      __u32 FormattedSize;
+-      unsigned char UnitSizeFactor;
+-} __attribute__((packed));
+-
+-#define MAX_ERASE_ZONES (8192 - 512)
+-
+-#define ERASE_MARK 0x3c69
+-#define SECTOR_FREE 0xff
+-#define SECTOR_USED 0x55
+-#define SECTOR_IGNORE 0x11
+-#define SECTOR_DELETED 0x00
+-
+-#define FOLD_MARK_IN_PROGRESS 0x5555
+-
+-#define ZONE_GOOD 0xff
+-#define ZONE_BAD_ORIGINAL 0
+-#define ZONE_BAD_MARKED 7
++#include <linux/mtd/blktrans.h>
+-#ifdef __KERNEL__
++#include <mtd/nftl-user.h>
+ /* these info are used in ReplUnitTable */
+ #define BLOCK_NIL          0xffff /* last block of a chain */
+@@ -84,8 +19,7 @@
+ #define BLOCK_RESERVED     0xfffc /* bios block or bad block */
+ struct NFTLrecord {
+-      struct mtd_info *mtd;
+-      struct semaphore mutex;
++      struct mtd_blktrans_dev mbd;
+       __u16 MediaUnit, SpareMediaUnit;
+       __u32 EraseSize;
+       struct NFTLMediaHeader MediaHdr;
+@@ -97,13 +31,13 @@
+       __u16 lastEUN;                  /* should be suppressed */
+       __u16 numfreeEUNs;
+       __u16 LastFreeEUN;              /* To speed up finding a free EUN */
+-      __u32 nr_sects;
+       int head,sect,cyl;
+       __u16 *EUNtable;                /* [numvunits]: First EUN for each virtual unit  */
+       __u16 *ReplUnitTable;           /* [numEUNs]: ReplUnitNumber for each */
+         unsigned int nb_blocks;               /* number of physical blocks */
+         unsigned int nb_boot_blocks;  /* number of blocks used by the bios */
+         struct erase_info instr;
++      struct nand_oobinfo oobinfo;
+ };
+ int NFTL_mount(struct NFTLrecord *s);
+@@ -114,9 +48,7 @@
+ #endif
+ #define MAX_NFTLS 16
+-#define MAX_SECTORS_PER_UNIT 32
++#define MAX_SECTORS_PER_UNIT 64
+ #define NFTL_PARTN_BITS 4
+-#endif /* __KERNEL__ */
+-
+ #endif /* __MTD_NFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/partitions.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/partitions.h
+@@ -5,7 +5,7 @@
+  *
+  * This code is GPL
+  *
+- * $Id$
++ * $Id$
+  */
+ #ifndef MTD_PARTITIONS_H
+@@ -41,6 +41,7 @@
+       u_int32_t size;         /* partition size */
+       u_int32_t offset;               /* offset within the master MTD space */
+       u_int32_t mask_flags;   /* master MTD flags to mask out for this partition */
++      struct nand_oobinfo *oobsel;    /* out of band layout for this partition (NAND only)*/
+       struct mtd_info **mtdp; /* pointer to store the MTD object */
+ };
+@@ -49,8 +50,26 @@
+ #define MTDPART_SIZ_FULL      (0)
+-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
+ int del_mtd_partitions(struct mtd_info *);
++/*
++ * Functions dealing with the various ways of partitioning the space
++ */
++
++struct mtd_part_parser {
++      struct list_head list;
++      struct module *owner;
++      const char *name;
++      int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
++};
++
++extern int register_mtd_parser(struct mtd_part_parser *parser);
++extern int deregister_mtd_parser(struct mtd_part_parser *parser);
++extern int parse_mtd_partitions(struct mtd_info *master, const char **types, 
++                              struct mtd_partition **pparts, unsigned long origin);
++
++#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
++
+ #endif
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/physmap.h
+@@ -0,0 +1,61 @@
++/*
++ * For boards with physically mapped flash and using 
++ * drivers/mtd/maps/physmap.c mapping driver.
++ *
++ * $Id$
++ *
++ * Copyright (C) 2003 MontaVista Software Inc.
++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
++ *
++ * 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.
++ *
++ */
++
++#ifndef __LINUX_MTD_PHYSMAP__
++
++#include <linux/config.h>
++
++#if defined(CONFIG_MTD_PHYSMAP) 
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++/*
++ * The map_info for physmap.  Board can override size, buswidth, phys,
++ * (*set_vpp)(), etc in their initial setup routine.
++ */
++extern struct map_info physmap_map;
++
++/*
++ * Board needs to specify the exact mapping during their setup time.
++ */
++static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
++{
++      physmap_map.phys = addr;
++      physmap_map.size = size;
++      physmap_map.bankwidth = bankwidth;
++      physmap_map.set_vpp = set_vpp;
++}
++
++#if defined(CONFIG_MTD_PARTITIONS)
++
++/*
++ * Machines that wish to do flash partition may want to call this function in 
++ * their setup routine.  
++ *
++ *    physmap_set_partitions(mypartitions, num_parts);
++ *
++ * Note that one can always override this hard-coded partition with 
++ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS).
++ */
++void physmap_set_partitions(struct mtd_partition *parts, int num_parts);
++
++#endif /* defined(CONFIG_MTD_PARTITIONS) */
++#endif /* defined(CONFIG_MTD) */
++
++#endif /* __LINUX_MTD_PHYSMAP__ */
++
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/plat-ram.h
+@@ -0,0 +1,35 @@
++/* linux/include/mtd/plat-ram.h
++ *
++ * (c) 2004 Simtec Electronics
++ *    http://www.simtec.co.uk/products/SWLINUX/
++ *    Ben Dooks <ben@simtec.co.uk>
++ *
++ * Generic platform 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 version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#ifndef __LINUX_MTD_PLATRAM_H
++#define __LINUX_MTD_PLATRAM_H __FILE__
++
++#define PLATRAM_RO (0)
++#define PLATRAM_RW (1)
++
++struct platdata_mtd_ram {
++      char                    *mapname;
++      char                   **probes;
++      struct mtd_partition    *partitions;
++      int                      nr_partitions;
++      int                      bankwidth;
++
++      /* control callbacks */
++
++      void    (*set_rw)(struct device *dev, int to);
++};
++
++#endif /* __LINUX_MTD_PLATRAM_H */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/xip.h
+@@ -0,0 +1,107 @@
++/*
++ * MTD primitives for XIP support
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Nov 2, 2004
++ * Copyright: (C) 2004 MontaVista Software, Inc.
++ *
++ * This XIP support for MTD has been loosely inspired
++ * by an earlier patch authored by David Woodhouse.
++ *
++ * 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$
++ */
++
++#ifndef __LINUX_MTD_XIP_H__
++#define __LINUX_MTD_XIP_H__
++
++#include <linux/config.h>
++
++#ifdef CONFIG_MTD_XIP
++
++/*
++ * Function that are modifying the flash state away from array mode must
++ * obviously not be running from flash.  The __xipram is therefore marking
++ * those functions so they get relocated to ram.
++ */
++#define __xipram __attribute__ ((__section__ (".data")))
++
++/*
++ * We really don't want gcc to guess anything.
++ * We absolutely _need_ proper inlining.
++ */
++#include <linux/compiler.h>
++
++/*
++ * Each architecture has to provide the following macros.  They must access
++ * the hardware directly and not rely on any other (XIP) functions since they
++ * won't be available when used (flash not in array mode).
++ *
++ * xip_irqpending()
++ *
++ *    return non zero when any hardware interrupt is pending.
++ *
++ * xip_currtime()
++ *
++ *    return a platform specific time reference to be used with
++ *    xip_elapsed_since().
++ *
++ * xip_elapsed_since(x)
++ *
++ *    return in usecs the elapsed timebetween now and the reference x as
++ *    returned by xip_currtime().
++ *
++ *    note 1: convertion to usec can be approximated, as long as the
++ *            returned value is <= the real elapsed time.
++ *    note 2: this should be able to cope with a few seconds without
++ *            overflowing.
++ */
++
++#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
++
++#include <asm/hardware.h>
++#ifdef CONFIG_ARCH_PXA
++#include <asm/arch/pxa-regs.h>
++#endif
++
++#define xip_irqpending()      (ICIP & ICMR)
++
++/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
++#define xip_currtime()                (OSCR)
++#define xip_elapsed_since(x)  (signed)((OSCR - (x)) / 4)
++
++#else
++
++#warning "missing IRQ and timer primitives for XIP MTD support"
++#warning "some of the XIP MTD support code will be disabled"
++#warning "your system will therefore be unresponsive when writing or erasing flash"
++
++#define xip_irqpending()      (0)
++#define xip_currtime()                (0)
++#define xip_elapsed_since(x)  (0)
++
++#endif
++
++/*
++ * xip_cpu_idle() is used when waiting for a delay equal or larger than
++ * the system timer tick period.  This should put the CPU into idle mode
++ * to save power and to be woken up only when some interrupts are pending.
++ * As above, this should not rely upon standard kernel code.
++ */
++
++#if defined(CONFIG_CPU_XSCALE)
++#define xip_cpu_idle()  asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
++#else
++#define xip_cpu_idle()  do { } while (0)
++#endif
++
++#else
++
++#define __xipram
++
++#endif /* CONFIG_MTD_XIP */
++
++#endif /* __LINUX_MTD_XIP_H__ */
+--- /dev/null
++++ linux-2.4.21/include/linux/pm-devices.h
+@@ -0,0 +1,41 @@
++#ifndef _LINUX_PM_DEV_H
++#define _LINUX_PM_DEV_H
++
++/*
++ * Copyright 2002 Montavista Software (mlocke@mvista.com)
++ *
++ * 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.
++ */
++
++
++
++/*
++ * Device types
++ */
++enum
++{
++        PM_UNKNOWN_DEV = 0, /* generic */
++        PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
++        PM_PCI_DEV,         /* PCI device */
++        PM_USB_DEV,         /* USB device */
++        PM_SCSI_DEV,        /* SCSI device */
++        PM_ISA_DEV,         /* ISA device */
++        PM_MTD_DEV,         /* Memory Technology Device */
++      PM_TPANEL_DEV,      /* Memory Technology Device */
++        PM_STORAGE_DEV,      /* Memory Technology Device */
++        PM_NETWORK_DEV,      /* Memory Technology Device */
++        PM_PCMCIA_DEV,      /* Memory Technology Device */
++        PM_DISPLAY_DEV,      /* Memory Technology Device */
++        PM_SERIAL_DEV,      /* Memory Technology Device */
++        PM_BATTERY_DEV,      /* Memory Technology Device */
++};
++
++#endif
+--- linux-2.4.21/include/linux/pm.h~pm
++++ linux-2.4.21/include/linux/pm.h
+@@ -24,6 +24,7 @@
+ #ifdef __KERNEL__
+ #include <linux/config.h>
++#include <linux/pm-devices.h>
+ #include <linux/list.h>
+ /*
+@@ -50,20 +51,6 @@
+ typedef int pm_request_t;
+-/*
+- * Device types
+- */
+-enum
+-{
+-      PM_UNKNOWN_DEV = 0, /* generic */
+-      PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
+-      PM_PCI_DEV,         /* PCI device */
+-      PM_USB_DEV,         /* USB device */
+-      PM_SCSI_DEV,        /* SCSI device */
+-      PM_ISA_DEV,         /* ISA device */
+-      PM_MTD_DEV,         /* Memory Technology Device */
+-};
+-
+ typedef int pm_dev_t;
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/rbtree-24.h
+@@ -0,0 +1,133 @@
++/*
++  Red Black Trees
++  (C) 1999  Andrea Arcangeli <andrea@suse.de>
++  
++  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
++
++  linux/include/linux/rbtree.h
++
++  To use rbtrees you'll have to implement your own insert and search cores.
++  This will avoid us to use callbacks and to drop drammatically performances.
++  I know it's not the cleaner way,  but in C (not in C++) to get
++  performances and genericity...
++
++  Some example of insert and search follows here. The search is a plain
++  normal search over an ordered tree. The insert instead must be implemented
++  int two steps: as first thing the code must insert the element in
++  order as a red leaf in the tree, then the support library function
++  rb_insert_color() must be called. Such function will do the
++  not trivial work to rebalance the rbtree if necessary.
++
++-----------------------------------------------------------------------
++static inline struct page * rb_search_page_cache(struct inode * inode,
++                                               unsigned long offset)
++{
++      rb_node_t * n = inode->i_rb_page_cache.rb_node;
++      struct page * page;
++
++      while (n)
++      {
++              page = rb_entry(n, struct page, rb_page_cache);
++
++              if (offset < page->offset)
++                      n = n->rb_left;
++              else if (offset > page->offset)
++                      n = n->rb_right;
++              else
++                      return page;
++      }
++      return NULL;
++}
++
++static inline struct page * __rb_insert_page_cache(struct inode * inode,
++                                                 unsigned long offset,
++                                                 rb_node_t * node)
++{
++      rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
++      rb_node_t * parent = NULL;
++      struct page * page;
++
++      while (*p)
++      {
++              parent = *p;
++              page = rb_entry(parent, struct page, rb_page_cache);
++
++              if (offset < page->offset)
++                      p = &(*p)->rb_left;
++              else if (offset > page->offset)
++                      p = &(*p)->rb_right;
++              else
++                      return page;
++      }
++
++      rb_link_node(node, parent, p);
++
++      return NULL;
++}
++
++static inline struct page * rb_insert_page_cache(struct inode * inode,
++                                               unsigned long offset,
++                                               rb_node_t * node)
++{
++      struct page * ret;
++      if ((ret = __rb_insert_page_cache(inode, offset, node)))
++              goto out;
++      rb_insert_color(node, &inode->i_rb_page_cache);
++ out:
++      return ret;
++}
++-----------------------------------------------------------------------
++*/
++
++#ifndef       _LINUX_RBTREE_H
++#define       _LINUX_RBTREE_H
++
++#include <linux/kernel.h>
++#include <linux/stddef.h>
++
++typedef struct rb_node_s
++{
++      struct rb_node_s * rb_parent;
++      int rb_color;
++#define       RB_RED          0
++#define       RB_BLACK        1
++      struct rb_node_s * rb_right;
++      struct rb_node_s * rb_left;
++}
++rb_node_t;
++
++typedef struct rb_root_s
++{
++      struct rb_node_s * rb_node;
++}
++rb_root_t;
++
++#define RB_ROOT       (rb_root_t) { NULL, }
++#define       rb_entry(ptr, type, member)                                     \
++      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++
++extern void rb_insert_color(rb_node_t *, rb_root_t *);
++extern void rb_erase(rb_node_t *, rb_root_t *);
++
++static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
++{
++      node->rb_parent = parent;
++      node->rb_color = RB_RED;
++      node->rb_left = node->rb_right = NULL;
++
++      *rb_link = node;
++}
++
++#endif        /* _LINUX_RBTREE_H */
+--- linux-2.4.21/include/linux/rbtree.h~mtd-cvs
++++ linux-2.4.21/include/linux/rbtree.h
+@@ -1,133 +1,25 @@
+ /*
+-  Red Black Trees
+-  (C) 1999  Andrea Arcangeli <andrea@suse.de>
+-  
+-  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
+-
+-  linux/include/linux/rbtree.h
+-
+-  To use rbtrees you'll have to implement your own insert and search cores.
+-  This will avoid us to use callbacks and to drop drammatically performances.
+-  I know it's not the cleaner way,  but in C (not in C++) to get
+-  performances and genericity...
+-
+-  Some example of insert and search follows here. The search is a plain
+-  normal search over an ordered tree. The insert instead must be implemented
+-  int two steps: as first thing the code must insert the element in
+-  order as a red leaf in the tree, then the support library function
+-  rb_insert_color() must be called. Such function will do the
+-  not trivial work to rebalance the rbtree if necessary.
+-
+------------------------------------------------------------------------
+-static inline struct page * rb_search_page_cache(struct inode * inode,
+-                                               unsigned long offset)
+-{
+-      rb_node_t * n = inode->i_rb_page_cache.rb_node;
+-      struct page * page;
+-
+-      while (n)
+-      {
+-              page = rb_entry(n, struct page, rb_page_cache);
+-
+-              if (offset < page->offset)
+-                      n = n->rb_left;
+-              else if (offset > page->offset)
+-                      n = n->rb_right;
+-              else
+-                      return page;
+-      }
+-      return NULL;
+-}
+-
+-static inline struct page * __rb_insert_page_cache(struct inode * inode,
+-                                                 unsigned long offset,
+-                                                 rb_node_t * node)
+-{
+-      rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
+-      rb_node_t * parent = NULL;
+-      struct page * page;
+-
+-      while (*p)
+-      {
+-              parent = *p;
+-              page = rb_entry(parent, struct page, rb_page_cache);
+-
+-              if (offset < page->offset)
+-                      p = &(*p)->rb_left;
+-              else if (offset > page->offset)
+-                      p = &(*p)->rb_right;
+-              else
+-                      return page;
+-      }
+-
+-      rb_link_node(node, parent, p);
+-
+-      return NULL;
+-}
+-
+-static inline struct page * rb_insert_page_cache(struct inode * inode,
+-                                               unsigned long offset,
+-                                               rb_node_t * node)
+-{
+-      struct page * ret;
+-      if ((ret = __rb_insert_page_cache(inode, offset, node)))
+-              goto out;
+-      rb_insert_color(node, &inode->i_rb_page_cache);
+- out:
+-      return ret;
+-}
+------------------------------------------------------------------------
+-*/
+-
+-#ifndef       _LINUX_RBTREE_H
+-#define       _LINUX_RBTREE_H
+-
+-#include <linux/kernel.h>
+-#include <linux/stddef.h>
+-
+-typedef struct rb_node_s
+-{
+-      struct rb_node_s * rb_parent;
+-      int rb_color;
+-#define       RB_RED          0
+-#define       RB_BLACK        1
+-      struct rb_node_s * rb_right;
+-      struct rb_node_s * rb_left;
+-}
+-rb_node_t;
++ * 2.5 compatibility
++ * $Id$
++ */
+-typedef struct rb_root_s
+-{
+-      struct rb_node_s * rb_node;
+-}
+-rb_root_t;
++#ifndef __MTD_COMPAT_RBTREE_H__
++#define __MTD_COMPAT_RBTREE_H__
+-#define RB_ROOT       (rb_root_t) { NULL, }
+-#define       rb_entry(ptr, type, member)                                     \
+-      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++#include <linux/version.h>
+-extern void rb_insert_color(rb_node_t *, rb_root_t *);
+-extern void rb_erase(rb_node_t *, rb_root_t *);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
++#include_next <linux/rbtree.h>
++#else
++#define rb_node_s rb_node
++#define rb_root_s rb_root
+-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
+-{
+-      node->rb_parent = parent;
+-      node->rb_color = RB_RED;
+-      node->rb_left = node->rb_right = NULL;
++#include <linux/rbtree-24.h>
+-      *rb_link = node;
+-}
++/* Find logical next and previous nodes in a tree */
++extern struct rb_node *rb_next(struct rb_node *);
++extern struct rb_node *rb_prev(struct rb_node *);
++extern struct rb_node *rb_first(struct rb_root *);
++#endif
+-#endif        /* _LINUX_RBTREE_H */
++#endif /*  __MTD_COMPAT_RBTREE_H__ */
+--- /dev/null
++++ linux-2.4.21/include/linux/rslib.h
+@@ -0,0 +1,105 @@
++/* 
++ * include/linux/rslib.h
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * RS code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
++ * $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.
++ */
++
++#ifndef _RSLIB_H_
++#define _RSLIB_H_
++
++#include <linux/list.h>
++
++/** 
++ * struct rs_control - rs control structure
++ * 
++ * @mm:               Bits per symbol
++ * @nn:               Symbols per block (= (1<<mm)-1)
++ * @alpha_to: log lookup table
++ * @index_of: Antilog lookup table
++ * @genpoly:  Generator polynomial 
++ * @nroots:   Number of generator roots = number of parity symbols
++ * @fcr:      First consecutive root, index form
++ * @prim:     Primitive element, index form 
++ * @iprim:    prim-th root of 1, index form 
++ * @gfpoly:   The primitive generator polynominal 
++ * @users:    Users of this structure 
++ * @list:     List entry for the rs control list
++*/
++struct rs_control {
++      int             mm;
++      int             nn;
++      uint16_t        *alpha_to;
++      uint16_t        *index_of;
++      uint16_t        *genpoly;
++      int             nroots;
++      int             fcr;
++      int             prim;
++      int             iprim;
++      int             gfpoly;
++      int             users;
++      struct list_head list;
++};
++
++/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit  */
++#ifdef CONFIG_REED_SOLOMON_ENC8
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
++             uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC8
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, 
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++             uint16_t *corr);
++#endif
++
++/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit  */
++#ifdef CONFIG_REED_SOLOMON_ENC16
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
++              uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC16
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++              uint16_t *corr);
++#endif
++
++/* Create or get a matching rs control structure */
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
++                         int nroots);
++
++/* Release a rs control structure */
++void free_rs(struct rs_control *rs);
++
++/** modulo replacement for galois field arithmetics
++ *
++ *  @rs:      the rs control structure
++ *  @x:               the value to reduce
++ *
++ *  where
++ *  rs->mm = number of bits per symbol        
++ *  rs->nn = (2^rs->mm) - 1
++ *  
++ *  Simple arithmetic modulo would return a wrong result for values
++ *  >= 3 * rs->nn
++*/
++static inline int rs_modnn(struct rs_control *rs, int x)
++{
++      while (x >= rs->nn) {
++              x -= rs->nn;
++              x = (x >> rs->mm) + (x & rs->nn);
++      }
++      return x;
++}
++
++#endif
+--- linux-2.4.21/include/linux/soundcard.h~ucb1x00
++++ linux-2.4.21/include/linux/soundcard.h
+@@ -811,6 +811,7 @@
+ #define SOUND_MIXER_STEREODEVS        0xfb    /* Mixer channels supporting stereo */
+ #define SOUND_MIXER_OUTSRC    0xfa    /* Arg contains a bit for each input source to output */
+ #define SOUND_MIXER_OUTMASK   0xf9    /* Arg contains a bit for each supported input source to output */
++#define SOUND_MIXER_AC97      0xf8    /* directly access ac97 registers */
+ /*    Device mask bits        */
+@@ -874,6 +875,7 @@
+ #define SOUND_MIXER_READ_RECMASK      MIXER_READ(SOUND_MIXER_RECMASK)
+ #define SOUND_MIXER_READ_STEREODEVS   MIXER_READ(SOUND_MIXER_STEREODEVS)
+ #define SOUND_MIXER_READ_CAPS         MIXER_READ(SOUND_MIXER_CAPS)
++#define SOUND_MIXER_READ_AC97         MIXER_READ(SOUND_MIXER_AC97)
+ #define MIXER_WRITE(dev)              _SIOWR('M', dev, int)
+ #define SOUND_MIXER_WRITE_VOLUME      MIXER_WRITE(SOUND_MIXER_VOLUME)
+@@ -900,6 +902,7 @@
+ #define SOUND_MIXER_WRITE_LOUD                MIXER_WRITE(SOUND_MIXER_LOUD)
+ #define SOUND_MIXER_WRITE_RECSRC      MIXER_WRITE(SOUND_MIXER_RECSRC)
++#define SOUND_MIXER_WRITE_AC97                MIXER_WRITE(SOUND_MIXER_AC97)
+ typedef struct mixer_info
+ {
+--- /dev/null
++++ linux-2.4.21/include/linux/suspend.h
+@@ -0,0 +1,10 @@
++/* $Id$ */
++
++#ifndef __MTD_COMPAT_VERSION_H__
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#include_next <linux/suspend.h>
++#endif
++
++#endif /* __MTD_COMPAT_VERSION_H__ */
+--- linux-2.4.21/include/linux/tty.h~ramses-lcd
++++ linux-2.4.21/include/linux/tty.h
+@@ -10,8 +10,8 @@
+  * resizing).
+  */
+ #define MIN_NR_CONSOLES 1       /* must be at least 1 */
+-#define MAX_NR_CONSOLES       63      /* serial lines start at 64 */
+-#define MAX_NR_USER_CONSOLES 63       /* must be root to allocate above this */
++#define MAX_NR_CONSOLES       3       /* serial lines start at 64 */
++#define MAX_NR_USER_CONSOLES 3        /* must be root to allocate above this */
+               /* Note: the ioctl VT_GETSTATE does not work for
+                  consoles 16 and higher (since it returns a short) */
+--- linux-2.4.21/include/linux/usb.h~ramses-usb
++++ linux-2.4.21/include/linux/usb.h
+@@ -1079,7 +1079,7 @@
+ void usb_show_string(struct usb_device *dev, char *id, int index);
+ #ifdef DEBUG
+-#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
++#define dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
+ #else
+ #define dbg(format, arg...) do {} while (0)
+ #endif
+--- linux-2.4.21/include/linux/wireless.h~linux-iw241_we16-6
++++ linux-2.4.21/include/linux/wireless.h
+@@ -1,7 +1,7 @@
+ /*
+  * This file define a set of standard wireless extensions
+  *
+- * Version :  15      12.7.02
++ * Version :  16      2.4.03
+  *
+  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+  * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -69,6 +69,8 @@
+ /***************************** INCLUDES *****************************/
++/* To minimise problems in user space, I might remove those headers
++ * at some point. Jean II */
+ #include <linux/types.h>              /* for "caddr_t" et al          */
+ #include <linux/socket.h>             /* for "struct sockaddr" et al  */
+ #include <linux/if.h>                 /* for IFNAMSIZ and co... */
+@@ -80,7 +82,7 @@
+  * (there is some stuff that will be added in the future...)
+  * I just plan to increment with each new version.
+  */
+-#define WIRELESS_EXT  15
++#define WIRELESS_EXT  16
+ /*
+  * Changes :
+@@ -163,6 +165,16 @@
+  *    - Add IW_TXPOW_RANGE for range of Tx Powers
+  *    - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+  *    - Add IW_MODE_MONITOR for passive monitor
++ *
++ * V15 to V16
++ * ----------
++ *    - Increase the number of bitrates in iw_range to 32 (for 802.11g)
++ *    - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
++ *    - Reshuffle struct iw_range for increases, add filler
++ *    - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
++ *    - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
++ *    - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
++ *    - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+  */
+ /**************************** CONSTANTS ****************************/
+@@ -196,9 +208,11 @@
+ /* SIOCGIWSTATS is strictly used between user space and the kernel, and
+  * is never passed to the driver (i.e. the driver will never see it). */
+-/* Mobile IP support (statistics per MAC address) */
++/* Spy support (statistics per MAC address - used for Mobile IP support) */
+ #define SIOCSIWSPY    0x8B10          /* set spy addresses */
+ #define SIOCGIWSPY    0x8B11          /* get spy info (quality of link) */
++#define SIOCSIWTHRSPY 0x8B12          /* set spy threshold (spy event) */
++#define SIOCGIWTHRSPY 0x8B13          /* get spy threshold */
+ /* Access Point manipulation */
+ #define SIOCSIWAP     0x8B14          /* set access point MAC addresses */
+@@ -294,7 +308,7 @@
+ #define IW_PRIV_TYPE_FLOAT    0x5000  /* struct iw_freq */
+ #define IW_PRIV_TYPE_ADDR     0x6000  /* struct sockaddr */
+-#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed nuber of args */
++#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed number of args */
+ #define IW_PRIV_SIZE_MASK     0x07FF  /* Max number of those args */
+@@ -306,13 +320,13 @@
+ /* ----------------------- OTHER CONSTANTS ----------------------- */
+ /* Maximum frequencies in the range struct */
+-#define IW_MAX_FREQUENCIES    16
++#define IW_MAX_FREQUENCIES    32
+ /* Note : if you have something like 80 frequencies,
+  * don't increase this constant and don't fill the frequency list.
+  * The user will be able to set by channel anyway... */
+ /* Maximum bit rates in the range struct */
+-#define IW_MAX_BITRATES               8
++#define IW_MAX_BITRATES               32
+ /* Maximum tx powers in the range struct */
+ #define IW_MAX_TXPOWER                8
+@@ -320,8 +334,7 @@
+  * a few of them in the struct iw_range. */
+ /* Maximum of address that you may set with SPY */
+-#define IW_MAX_SPY            8       /* set */
+-#define IW_MAX_GET_SPY                64      /* get */
++#define IW_MAX_SPY            8
+ /* Maximum of address that you may get in the
+    list of access points in range */
+@@ -354,7 +367,8 @@
+ #define IW_ENCODE_ENABLED     0x0000  /* Encoding enabled */
+ #define IW_ENCODE_RESTRICTED  0x4000  /* Refuse non-encoded packets */
+ #define IW_ENCODE_OPEN                0x2000  /* Accept non-encoded packets */
+-#define IW_ENCODE_NOKEY         0x0800  /* Key is write only, so not present */
++#define IW_ENCODE_NOKEY               0x0800  /* Key is write only, so not present */
++#define IW_ENCODE_TEMP                0x0400  /* Temporary key */
+ /* Power management flags available (along with the value, if any) */
+ #define IW_POWER_ON           0x0000  /* No details... */
+@@ -482,6 +496,17 @@
+       __u32           beacon;         /* Missed beacons/superframe */
+ };
++/*
++ *    Quality range (for spy threshold)
++ */
++struct        iw_thrspy
++{
++      struct sockaddr         addr;           /* Source address (hw/mac) */
++      struct iw_quality       qual;           /* Quality of the link */
++      struct iw_quality       low;            /* Low threshold */
++      struct iw_quality       high;           /* High threshold */
++};
++
+ /* ------------------------ WIRELESS STATS ------------------------ */
+ /*
+  * Wireless statistics (used for /proc/net/wireless)
+@@ -534,7 +559,7 @@
+       struct iw_quality qual;         /* Quality part of statistics */
+       struct sockaddr ap_addr;        /* Access point address */
+-      struct sockaddr addr;           /* Destination address (hw) */
++      struct sockaddr addr;           /* Destination address (hw/mac) */
+       struct iw_param param;          /* Other small parameters */
+       struct iw_point data;           /* Other large parameters */
+@@ -582,17 +607,31 @@
+       __u32           min_nwid;       /* Minimal NWID we are able to set */
+       __u32           max_nwid;       /* Maximal NWID we are able to set */
+-      /* Frequency */
+-      __u16           num_channels;   /* Number of channels [0; num - 1] */
+-      __u8            num_frequency;  /* Number of entry in the list */
+-      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
+-      /* Note : this frequency list doesn't need to fit channel numbers */
++      /* Old Frequency (backward compat - moved lower ) */
++      __u16           old_num_channels;
++      __u8            old_num_frequency;
++      /* Filler to keep "version" at the same offset */
++      __s32           old_freq[6];
+       /* signal level threshold range */
+       __s32   sensitivity;
+       /* Quality of link & SNR stuff */
++      /* Quality range (link, level, noise)
++       * If the quality is absolute, it will be in the range [0 ; max_qual],
++       * if the quality is dBm, it will be in the range [max_qual ; 0].
++       * Don't forget that we use 8 bit arithmetics... */
+       struct iw_quality       max_qual;       /* Quality of the link */
++      /* This should contain the average/typical values of the quality
++       * indicator. This should be the threshold between a "good" and
++       * a "bad" link (example : monitor going from green to orange).
++       * Currently, user space apps like quality monitors don't have any
++       * way to calibrate the measurement. With this, they can split
++       * the range between 0 and max_qual in different quality level
++       * (using a geometric subdivision centered on the average).
++       * I expect that people doing the user space apps will feedback
++       * us on which value we need to put in each driver... */
++      struct iw_quality       avg_qual;       /* Quality of the link */
+       /* Rates */
+       __u8            num_bitrates;   /* Number of entries in the list */
+@@ -619,6 +658,8 @@
+       __u16   encoding_size[IW_MAX_ENCODING_SIZES];   /* Different token sizes */
+       __u8    num_encoding_sizes;     /* Number of entry in the list */
+       __u8    max_encoding_tokens;    /* Max number of tokens */
++      /* For drivers that need a "login/passwd" form */
++      __u8    encoding_login_index;   /* token index for login token */
+       /* Transmit power */
+       __u16           txpower_capa;   /* What options are supported */
+@@ -638,18 +679,12 @@
+       __s32           min_r_time;     /* Minimal retry lifetime */
+       __s32           max_r_time;     /* Maximal retry lifetime */
+-      /* Average quality of link & SNR */
+-      struct iw_quality       avg_qual;       /* Quality of the link */
+-      /* This should contain the average/typical values of the quality
+-       * indicator. This should be the threshold between a "good" and
+-       * a "bad" link (example : monitor going from green to orange).
+-       * Currently, user space apps like quality monitors don't have any
+-       * way to calibrate the measurement. With this, they can split
+-       * the range between 0 and max_qual in different quality level
+-       * (using a geometric subdivision centered on the average).
+-       * I expect that people doing the user space apps will feedback
+-       * us on which value we need to put in each driver...
+-       */
++      /* Frequency */
++      __u16           num_channels;   /* Number of channels [0; num - 1] */
++      __u8            num_frequency;  /* Number of entry in the list */
++      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
++      /* Note : this frequency list doesn't need to fit channel numbers,
++       * because each entry contain its channel index */
+ };
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/workqueue.h
+@@ -0,0 +1,21 @@
++/*
++ * 2.5 compatibility 
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_WORKQUEUE_H__
++#define __MTD_COMPAT_WORKQUEUE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
++#include_next <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#define work_struct tq_struct
++#define schedule_work(x) schedule_task(x)
++#define flush_scheduled_work flush_scheduled_tasks
++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z)
++#endif
++
++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/inftl-user.h
+@@ -0,0 +1,91 @@
++/*
++ * $Id$
++ *
++ * Parts of INFTL headers shared with userspace 
++ *
++ */
++
++#ifndef __MTD_INFTL_USER_H__
++#define __MTD_INFTL_USER_H__
++
++#define       OSAK_VERSION    0x5120
++#define       PERCENTUSED     98
++
++#define       SECTORSIZE      512
++
++/* Block Control Information */
++
++struct inftl_bci {
++      uint8_t ECCsig[6];
++      uint8_t Status;
++      uint8_t Status1;
++} __attribute__((packed));
++
++struct inftl_unithead1 {
++      uint16_t virtualUnitNo;
++      uint16_t prevUnitNo;
++      uint8_t ANAC;
++      uint8_t NACs;
++      uint8_t parityPerField;
++      uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unithead2 {
++      uint8_t parityPerField;
++      uint8_t ANAC;
++      uint16_t prevUnitNo;
++      uint16_t virtualUnitNo;
++      uint8_t NACs;
++      uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unittail {
++      uint8_t Reserved[4];
++      uint16_t EraseMark;
++      uint16_t EraseMark1;
++} __attribute__((packed));
++
++union inftl_uci {
++      struct inftl_unithead1 a;
++      struct inftl_unithead2 b;
++      struct inftl_unittail c;
++};
++
++struct inftl_oob {
++      struct inftl_bci b;
++      union inftl_uci u;
++};
++
++
++/* INFTL Media Header */
++
++struct INFTLPartition {
++      __u32 virtualUnits;
++      __u32 firstUnit;
++      __u32 lastUnit;
++      __u32 flags;
++      __u32 spareUnits;
++      __u32 Reserved0;
++      __u32 Reserved1;
++} __attribute__((packed));
++
++struct INFTLMediaHeader {
++      char bootRecordID[8];
++      __u32 NoOfBootImageBlocks;
++      __u32 NoOfBinaryPartitions;
++      __u32 NoOfBDTLPartitions;
++      __u32 BlockMultiplierBits;
++      __u32 FormatFlags;
++      __u32 OsakVersion;
++      __u32 PercentUsed;
++      struct INFTLPartition Partitions[4];
++} __attribute__((packed));
++
++/* Partition flag types */
++#define       INFTL_BINARY    0x20000000
++#define       INFTL_BDTL      0x40000000
++#define       INFTL_LAST      0x80000000
++
++#endif /* __MTD_INFTL_USER_H__ */
++
++
+--- /dev/null
++++ linux-2.4.21/include/mtd/jffs2-user.h
+@@ -0,0 +1,35 @@
++/*
++ * $Id$
++ *
++ * JFFS2 definitions for use in user space only
++ */
++
++#ifndef __JFFS2_USER_H__
++#define __JFFS2_USER_H__
++
++/* This file is blessed for inclusion by userspace */
++#include <linux/jffs2.h>
++#include <endian.h>
++#include <byteswap.h>
++
++#undef cpu_to_je16
++#undef cpu_to_je32
++#undef cpu_to_jemode
++#undef je16_to_cpu
++#undef je32_to_cpu
++#undef jemode_to_cpu
++
++extern int target_endian;
++
++#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
++#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
++
++#define cpu_to_je16(x) ((jint16_t){t16(x)})
++#define cpu_to_je32(x) ((jint32_t){t32(x)})
++#define cpu_to_jemode(x) ((jmode_t){t32(x)})
++
++#define je16_to_cpu(x) (t16((x).v16))
++#define je32_to_cpu(x) (t32((x).v32))
++#define jemode_to_cpu(x) (t32((x).m))
++
++#endif /* __JFFS2_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-abi.h
+@@ -0,0 +1,119 @@
++/*
++ * $Id$
++ *
++ * Portions of MTD ABI definition which are shared by kernel and user space 
++ */
++
++#ifndef __MTD_ABI_H__
++#define __MTD_ABI_H__
++
++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
++                  separate files was to avoid #ifdef __KERNEL__ */
++#define __user
++#endif
++
++struct erase_info_user {
++      uint32_t start;
++      uint32_t length;
++};
++
++struct mtd_oob_buf {
++      uint32_t start;
++      uint32_t length;
++      unsigned char __user *ptr;
++};
++
++#define MTD_ABSENT            0
++#define MTD_RAM                       1
++#define MTD_ROM                       2
++#define MTD_NORFLASH          3
++#define MTD_NANDFLASH         4
++#define MTD_PEROM             5
++#define MTD_DATAFLASH         6
++#define MTD_OTHER             14
++#define MTD_UNKNOWN           15
++
++#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
++#define MTD_SET_BITS          2       // Bits can be set
++#define MTD_ERASEABLE         4       // Has an erase function
++#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
++#define MTD_VOLATILE          16      // Set for RAMs
++#define MTD_XIP                       32      // eXecute-In-Place possible
++#define MTD_OOB                       64      // Out-of-band data (NAND flash)
++#define MTD_ECC                       128     // Device capable of automatic ECC
++#define MTD_NO_VIRTBLOCKS     256     // Virtual blocks not allowed
++
++// Some common devices / combinations of capabilities
++#define MTD_CAP_ROM           0
++#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
++#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
++#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
++#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
++
++
++// Types of automatic ECC/Checksum available
++#define MTD_ECC_NONE          0       // No automatic ECC available
++#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
++#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
++
++/* ECC byte placement */
++#define MTD_NANDECC_OFF               0       // Switch off ECC (Not recommended)
++#define MTD_NANDECC_PLACE     1       // Use the given placement in the structure (YAFFS1 legacy mode)
++#define MTD_NANDECC_AUTOPLACE 2       // Use the default placement scheme
++#define MTD_NANDECC_PLACEONLY 3       // Use the given placement in the structure (Do not store ecc result on read)
++
++/* OTP mode selection */
++#define MTD_OTP_OFF           0
++#define MTD_OTP_FACTORY               1
++#define MTD_OTP_USER          2
++
++struct mtd_info_user {
++      uint8_t type;
++      uint32_t flags;
++      uint32_t size;   // Total size of the MTD
++      uint32_t erasesize;
++      uint32_t oobblock;  // Size of OOB blocks (e.g. 512)
++      uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
++      uint32_t ecctype;
++      uint32_t eccsize;
++};
++
++struct region_info_user {
++      uint32_t offset;                /* At which this region starts, 
++                                       * from the beginning of the MTD */
++      uint32_t erasesize;             /* For this region */
++      uint32_t numblocks;             /* Number of blocks in this region */
++      uint32_t regionindex;
++};
++
++struct otp_info {
++      uint32_t start;
++      uint32_t length;
++      uint32_t locked;
++};
++
++#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
++#define MEMERASE                _IOW('M', 2, struct erase_info_user)
++#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
++#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
++#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
++#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
++#define MEMGETREGIONCOUNT     _IOR('M', 7, int)
++#define MEMGETREGIONINFO      _IOWR('M', 8, struct region_info_user)
++#define MEMSETOOBSEL          _IOW('M', 9, struct nand_oobinfo)
++#define MEMGETOOBSEL          _IOR('M', 10, struct nand_oobinfo)
++#define MEMGETBADBLOCK                _IOW('M', 11, loff_t)
++#define MEMSETBADBLOCK                _IOW('M', 12, loff_t)
++#define OTPSELECT             _IOR('M', 13, int)
++#define OTPGETREGIONCOUNT     _IOW('M', 14, int)
++#define OTPGETREGIONINFO      _IOW('M', 15, struct otp_info)
++#define OTPLOCK               _IOR('M', 16, struct otp_info)
++
++struct nand_oobinfo {
++      uint32_t useecc;
++      uint32_t eccbytes;
++      uint32_t oobfree[8][2];
++      uint32_t eccpos[32];
++};
++
++#endif /* __MTD_ABI_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-user.h
+@@ -0,0 +1,20 @@
++/*
++ * $Id$
++ *
++ * MTD ABI header for use by user space only.
++ */
++
++#ifndef __MTD_USER_H__
++#define __MTD_USER_H__
++
++#include <stdint.h>
++
++/* This file is blessed for inclusion by userspace */
++#include <mtd/mtd-abi.h>
++
++typedef struct mtd_info_user mtd_info_t;
++typedef struct erase_info_user erase_info_t;
++typedef struct region_info_user region_info_t;
++typedef struct nand_oobinfo nand_oobinfo_t;
++
++#endif /* __MTD_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/nftl-user.h
+@@ -0,0 +1,76 @@
++/*
++ * $Id$
++ *
++ * Parts of NFTL headers shared with userspace 
++ *
++ */
++
++#ifndef __MTD_NFTL_USER_H__
++#define __MTD_NFTL_USER_H__
++
++/* Block Control Information */
++
++struct nftl_bci {
++      unsigned char ECCSig[6];
++      uint8_t Status;
++      uint8_t Status1;
++}__attribute__((packed));
++
++/* Unit Control Information */
++
++struct nftl_uci0 {
++      uint16_t VirtUnitNum;
++      uint16_t ReplUnitNum;
++      uint16_t SpareVirtUnitNum;
++      uint16_t SpareReplUnitNum;
++} __attribute__((packed));
++
++struct nftl_uci1 {
++      uint32_t WearInfo;
++      uint16_t EraseMark;
++      uint16_t EraseMark1;
++} __attribute__((packed));
++
++struct nftl_uci2 {
++        uint16_t FoldMark;
++        uint16_t FoldMark1;
++      uint32_t unused;
++} __attribute__((packed));
++
++union nftl_uci {
++      struct nftl_uci0 a;
++      struct nftl_uci1 b;
++      struct nftl_uci2 c;
++};
++
++struct nftl_oob {
++      struct nftl_bci b;
++      union nftl_uci u;
++};
++
++/* NFTL Media Header */
++
++struct NFTLMediaHeader {
++      char DataOrgID[6];
++      uint16_t NumEraseUnits;
++      uint16_t FirstPhysicalEUN;
++      uint32_t FormattedSize;
++      unsigned char UnitSizeFactor;
++} __attribute__((packed));
++
++#define MAX_ERASE_ZONES (8192 - 512)
++
++#define ERASE_MARK 0x3c69
++#define SECTOR_FREE 0xff
++#define SECTOR_USED 0x55
++#define SECTOR_IGNORE 0x11
++#define SECTOR_DELETED 0x00
++
++#define FOLD_MARK_IN_PROGRESS 0x5555
++
++#define ZONE_GOOD 0xff
++#define ZONE_BAD_ORIGINAL 0
++#define ZONE_BAD_MARKED 7
++
++
++#endif /* __MTD_NFTL_USER_H__ */
+--- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
++++ linux-2.4.21/include/net/iw_handler.h
+@@ -1,7 +1,7 @@
+ /*
+  * This file define the new driver API for Wireless Extensions
+  *
+- * Version :  4       21.6.02
++ * Version :  5       4.12.02
+  *
+  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+  * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -206,7 +206,7 @@
+  * will be needed...
+  * I just plan to increment with each new version.
+  */
+-#define IW_HANDLER_VERSION    4
++#define IW_HANDLER_VERSION    5
+ /*
+  * Changes :
+@@ -220,10 +220,18 @@
+  * V3 to V4
+  * --------
+  *    - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
++ *
++ * V4 to V5
++ * --------
++ *    - Add new spy support : struct iw_spy_data & prototypes
+  */
+ /**************************** CONSTANTS ****************************/
++/* Enable enhanced spy support. Disable to reduce footprint */
++#define IW_WIRELESS_SPY
++#define IW_WIRELESS_THRSPY
++
+ /* Special error message for the driver to indicate that we
+  * should do a commit after return from the iw_handler */
+ #define EIWCOMMIT     EINPROGRESS
+@@ -315,6 +323,9 @@
+        * We will automatically export that to user space... */
+       struct iw_priv_args *   private_args;
++      /* Driver enhanced spy support */
++      long                    spy_offset;     /* Spy data offset */
++
+       /* In the long term, get_wireless_stats will move from
+        * 'struct net_device' to here, to minimise bloat. */
+ };
+@@ -350,6 +361,33 @@
+ /* Need to think of short header translation table. Later. */
++/* --------------------- ENHANCED SPY SUPPORT --------------------- */
++/*
++ * In the old days, the driver was handling spy support all by itself.
++ * Now, the driver can delegate this task to Wireless Extensions.
++ * It needs to include this struct in its private part and use the
++ * standard spy iw_handler.
++ */
++
++/*
++ * Instance specific spy data, i.e. addresses spied and quality for them.
++ */
++struct iw_spy_data
++{
++#ifdef IW_WIRELESS_SPY
++      /* --- Standard spy support --- */
++      int                     spy_number;
++      u_char                  spy_address[IW_MAX_SPY][ETH_ALEN];
++      struct iw_quality       spy_stat[IW_MAX_SPY];
++#ifdef IW_WIRELESS_THRSPY
++      /* --- Enhanced spy support (event) */
++      struct iw_quality       spy_thr_low;    /* Low threshold */
++      struct iw_quality       spy_thr_high;   /* High threshold */
++      u_char                  spy_thr_under[IW_MAX_SPY];
++#endif /* IW_WIRELESS_THRSPY */
++#endif /* IW_WIRELESS_SPY */
++};
++
+ /**************************** PROTOTYPES ****************************/
+ /*
+  * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
+@@ -376,6 +414,31 @@
+ /* We may need a function to send a stream of events to user space.
+  * More on that later... */
++/* Standard handler for SIOCSIWSPY */
++extern int iw_handler_set_spy(struct net_device *     dev,
++                            struct iw_request_info *  info,
++                            union iwreq_data *        wrqu,
++                            char *                    extra);
++/* Standard handler for SIOCGIWSPY */
++extern int iw_handler_get_spy(struct net_device *     dev,
++                            struct iw_request_info *  info,
++                            union iwreq_data *        wrqu,
++                            char *                    extra);
++/* Standard handler for SIOCSIWTHRSPY */
++extern int iw_handler_set_thrspy(struct net_device *  dev,
++                               struct iw_request_info *info,
++                               union iwreq_data *     wrqu,
++                               char *                 extra);
++/* Standard handler for SIOCGIWTHRSPY */
++extern int iw_handler_get_thrspy(struct net_device *  dev,
++                               struct iw_request_info *info,
++                               union iwreq_data *     wrqu,
++                               char *                 extra);
++/* Driver call to update spy records */
++extern void wireless_spy_update(struct net_device *   dev,
++                              unsigned char *         address,
++                              struct iw_quality *     wstats);
++
+ /************************* INLINE FUNTIONS *************************/
+ /*
+  * Function that are so simple that it's more efficient inlining them
+--- linux-2.4.21/init/do_mounts.c~small-nocramdisk
++++ linux-2.4.21/init/do_mounts.c
+@@ -16,8 +16,6 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
+-#define BUILD_CRAMDISK
+-
+ extern int get_filesystem_list(char * buf);
+ extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
+--- linux-2.4.21/kernel/ksyms.c~ramses
++++ linux-2.4.21/kernel/ksyms.c
+@@ -585,6 +585,11 @@
+ EXPORT_SYMBOL(tasklist_lock);
+ EXPORT_SYMBOL(pidhash);
++#ifdef CONFIG_ARCH_RAMSES
++#include <asm/arch/ramses.h>
++EXPORT_SYMBOL(ramses_control_shadow);
++EXPORT_SYMBOL(ramses_flags);
++#endif
+ /* debug */
+ EXPORT_SYMBOL(dump_stack);
+--- linux-2.4.21/kernel/pm.c~pm
++++ linux-2.4.21/kernel/pm.c
+@@ -234,7 +234,7 @@
+       struct list_head *entry;
+       
+       down(&pm_devs_lock);
+-      entry = pm_devs.next;
++      entry = (rqst==PM_RESUME) ? pm_devs.prev : pm_devs.next;
+       while (entry != &pm_devs) {
+               struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
+               if (dev->callback) {
+@@ -249,7 +249,7 @@
+                               return status;
+                       }
+               }
+-              entry = entry->next;
++              entry = (rqst==PM_RESUME) ? entry->prev : entry->next;
+       }
+       up(&pm_devs_lock);
+       return 0;
+--- linux-2.4.21/lib/Config.in~mtd-cvs
++++ linux-2.4.21/lib/Config.in
+@@ -35,4 +35,20 @@
+   fi
+ fi
++if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
++     "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
++     "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
++   define_tristate CONFIG_REED_SOLOMON y
++   define_tristate CONFIG_REED_SOLOMON_DEC16 y
++else
++  if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
++     "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
++     "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
++     define_tristate CONFIG_REED_SOLOMON m
++     define_tristate CONFIG_REED_SOLOMON_DEC16 y
++  else
++     define_tristate CONFIG_REED_SOLOMON n
++  fi
++fi
++
+ endmenu
+--- linux-2.4.21/lib/Makefile~mtd-cvs
++++ linux-2.4.21/lib/Makefile
+@@ -22,6 +22,7 @@
+ subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
+ subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
++subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
+ # Include the subdirs, if necessary.
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/Makefile
+@@ -0,0 +1,12 @@
++#
++# This is a modified version of reed solomon lib, 
++#
++
++O_TARGET:= reed_solomon.o     
++
++export-objs := rslib.o
++
++obj-y := rslib.o
++obj-m := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/decode_rs.c
+@@ -0,0 +1,272 @@
++/* 
++ * lib/reed_solomon/decode_rs.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
++
++/* Generic data width independent code which is included by the 
++ * wrappers.
++ */
++{ 
++      int deg_lambda, el, deg_omega;
++      int i, j, r, k, pad;
++      int nn = rs->nn;
++      int nroots = rs->nroots;
++      int fcr = rs->fcr;
++      int prim = rs->prim;
++      int iprim = rs->iprim;
++      uint16_t *alpha_to = rs->alpha_to;
++      uint16_t *index_of = rs->index_of;
++      uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
++      /* Err+Eras Locator poly and syndrome poly The maximum value
++       * of nroots is 8. So the necessary stack size will be about
++       * 220 bytes max.
++       */
++      uint16_t lambda[nroots + 1], syn[nroots];
++      uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
++      uint16_t root[nroots], reg[nroots + 1], loc[nroots];
++      int count = 0;
++      uint16_t msk = (uint16_t) rs->nn;
++
++      /* Check length parameter for validity */
++      pad = nn - nroots - len;
++      if (pad < 0 || pad >= nn)
++              return -ERANGE;
++              
++      /* Does the caller provide the syndrome ? */
++      if (s != NULL) 
++              goto decode;
++
++      /* form the syndromes; i.e., evaluate data(x) at roots of
++       * g(x) */
++      for (i = 0; i < nroots; i++)
++              syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
++
++      for (j = 1; j < len; j++) {
++              for (i = 0; i < nroots; i++) {
++                      if (syn[i] == 0) {
++                              syn[i] = (((uint16_t) data[j]) ^ 
++                                        invmsk) & msk;
++                      } else {
++                              syn[i] = ((((uint16_t) data[j]) ^
++                                         invmsk) & msk) ^ 
++                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
++                                                     (fcr + i) * prim)];
++                      }
++              }
++      }
++
++      for (j = 0; j < nroots; j++) {
++              for (i = 0; i < nroots; i++) {
++                      if (syn[i] == 0) {
++                              syn[i] = ((uint16_t) par[j]) & msk;
++                      } else {
++                              syn[i] = (((uint16_t) par[j]) & msk) ^ 
++                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
++                                                     (fcr+i)*prim)];
++                      }
++              }
++      }
++      s = syn;
++
++      /* Convert syndromes to index form, checking for nonzero condition */
++      syn_error = 0;
++      for (i = 0; i < nroots; i++) {
++              syn_error |= s[i];
++              s[i] = index_of[s[i]];
++      }
++
++      if (!syn_error) {
++              /* if syndrome is zero, data[] is a codeword and there are no
++               * errors to correct. So return data[] unmodified
++               */
++              count = 0;
++              goto finish;
++      }
++
++ decode:
++      memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
++      lambda[0] = 1;
++
++      if (no_eras > 0) {
++              /* Init lambda to be the erasure locator polynomial */
++              lambda[1] = alpha_to[rs_modnn(rs, 
++                                            prim * (nn - 1 - eras_pos[0]))];
++              for (i = 1; i < no_eras; i++) {
++                      u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
++                      for (j = i + 1; j > 0; j--) {
++                              tmp = index_of[lambda[j - 1]];
++                              if (tmp != nn) {
++                                      lambda[j] ^= 
++                                              alpha_to[rs_modnn(rs, u + tmp)];
++                              }
++                      }
++              }
++      }
++
++      for (i = 0; i < nroots + 1; i++)
++              b[i] = index_of[lambda[i]];
++
++      /*
++       * Begin Berlekamp-Massey algorithm to determine error+erasure
++       * locator polynomial
++       */
++      r = no_eras;
++      el = no_eras;
++      while (++r <= nroots) { /* r is the step number */
++              /* Compute discrepancy at the r-th step in poly-form */
++              discr_r = 0;
++              for (i = 0; i < r; i++) {
++                      if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
++                              discr_r ^= 
++                                      alpha_to[rs_modnn(rs, 
++                                                        index_of[lambda[i]] +
++                                                        s[r - i - 1])];
++                      }
++              }
++              discr_r = index_of[discr_r];    /* Index form */
++              if (discr_r == nn) {
++                      /* 2 lines below: B(x) <-- x*B(x) */
++                      memmove (&b[1], b, nroots * sizeof (b[0]));
++                      b[0] = nn;
++              } else {
++                      /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
++                      t[0] = lambda[0];
++                      for (i = 0; i < nroots; i++) {
++                              if (b[i] != nn) {
++                                      t[i + 1] = lambda[i + 1] ^ 
++                                              alpha_to[rs_modnn(rs, discr_r +
++                                                                b[i])];
++                              } else
++                                      t[i + 1] = lambda[i + 1];
++                      }
++                      if (2 * el <= r + no_eras - 1) {
++                              el = r + no_eras - el;
++                              /*
++                               * 2 lines below: B(x) <-- inv(discr_r) *
++                               * lambda(x)
++                               */
++                              for (i = 0; i <= nroots; i++) {
++                                      b[i] = (lambda[i] == 0) ? nn :
++                                              rs_modnn(rs, index_of[lambda[i]]
++                                                       - discr_r + nn);
++                              }
++                      } else {
++                              /* 2 lines below: B(x) <-- x*B(x) */
++                              memmove(&b[1], b, nroots * sizeof(b[0]));
++                              b[0] = nn;
++                      }
++                      memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
++              }
++      }
++
++      /* Convert lambda to index form and compute deg(lambda(x)) */
++      deg_lambda = 0;
++      for (i = 0; i < nroots + 1; i++) {
++              lambda[i] = index_of[lambda[i]];
++              if (lambda[i] != nn)
++                      deg_lambda = i;
++      }
++      /* Find roots of error+erasure locator polynomial by Chien search */
++      memcpy(&reg[1], &lambda[1], nroots * sizeof(reg[0]));
++      count = 0;              /* Number of roots of lambda(x) */
++      for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
++              q = 1;          /* lambda[0] is always 0 */
++              for (j = deg_lambda; j > 0; j--) {
++                      if (reg[j] != nn) {
++                              reg[j] = rs_modnn(rs, reg[j] + j);
++                              q ^= alpha_to[reg[j]];
++                      }
++              }
++              if (q != 0)
++                      continue;       /* Not a root */
++              /* store root (index-form) and error location number */
++              root[count] = i;
++              loc[count] = k;
++              /* If we've already found max possible roots,
++               * abort the search to save time
++               */
++              if (++count == deg_lambda)
++                      break;
++      }
++      if (deg_lambda != count) {
++              /*
++               * deg(lambda) unequal to number of roots => uncorrectable
++               * error detected
++               */
++              count = -1;
++              goto finish;
++      }
++      /*
++       * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
++       * x**nroots). in index form. Also find deg(omega).
++       */
++      deg_omega = deg_lambda - 1;
++      for (i = 0; i <= deg_omega; i++) {
++              tmp = 0;
++              for (j = i; j >= 0; j--) {
++                      if ((s[i - j] != nn) && (lambda[j] != nn))
++                              tmp ^=
++                                  alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
++              }
++              omega[i] = index_of[tmp];
++      }
++
++      /*
++       * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
++       * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
++       */
++      for (j = count - 1; j >= 0; j--) {
++              num1 = 0;
++              for (i = deg_omega; i >= 0; i--) {
++                      if (omega[i] != nn)
++                              num1 ^= alpha_to[rs_modnn(rs, omega[i] + 
++                                                      i * root[j])];
++              }
++              num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
++              den = 0;
++
++              /* lambda[i+1] for i even is the formal derivative
++               * lambda_pr of lambda[i] */
++              for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
++                      if (lambda[i + 1] != nn) {
++                              den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + 
++                                                     i * root[j])];
++                      }
++              }
++              /* Apply error to data */
++              if (num1 != 0 && loc[j] >= pad) {
++                      uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] + 
++                                                     index_of[num2] +
++                                                     nn - index_of[den])];
++                      /* Store the error correction pattern, if a
++                       * correction buffer is available */
++                      if (corr) {
++                              corr[j] = cor;
++                      } else {
++                              /* If a data buffer is given and the
++                               * error is inside the message,
++                               * correct it */
++                              if (data && (loc[j] < (nn - nroots)))
++                                      data[loc[j] - pad] ^= cor;
++                      }
++              }
++      }
++
++finish:
++      if (eras_pos != NULL) {
++              for (i = 0; i < count; i++)
++                      eras_pos[i] = loc[i] - pad;
++      }
++      return count;
++
++}
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/encode_rs.c
+@@ -0,0 +1,54 @@
++/* 
++ * lib/reed_solomon/encode_rs.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
++
++/* Generic data width independent code which is included by the 
++ * wrappers.
++ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
++ */
++{
++      int i, j, pad;
++      int nn = rs->nn;
++      int nroots = rs->nroots;
++      uint16_t *alpha_to = rs->alpha_to;
++      uint16_t *index_of = rs->index_of;
++      uint16_t *genpoly = rs->genpoly;
++      uint16_t fb;
++      uint16_t msk = (uint16_t) rs->nn;
++
++      /* Check length parameter for validity */
++      pad = nn - nroots - len;
++      if (pad < 0 || pad >= nn)
++              return -ERANGE;
++
++      for (i = 0; i < len; i++) {
++              fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
++              /* feedback term is non-zero */
++              if (fb != nn) { 
++                      for (j = 1; j < nroots; j++) {
++                              par[j] ^= alpha_to[rs_modnn(rs, fb + 
++                                                       genpoly[nroots - j])];
++                      }
++              }
++              /* Shift */
++              memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
++              if (fb != nn) {
++                      par[nroots - 1] = alpha_to[rs_modnn(rs, 
++                                                          fb + genpoly[0])];
++              } else {
++                      par[nroots - 1] = 0;
++              }
++      }
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/rslib.c
+@@ -0,0 +1,335 @@
++/* 
++ * lib/reed_solomon/rslib.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * Reed Solomon code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
++ * $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:
++ *    
++ * The generic Reed Solomon library provides runtime configurable
++ * encoding / decoding of RS codes.
++ * Each user must call init_rs to get a pointer to a rs_control
++ * structure for the given rs parameters. This structure is either
++ * generated or a already available matching control structure is used.
++ * If a structure is generated then the polynomial arrays for
++ * fast encoding / decoding are built. This can take some time so
++ * make sure not to call this function from a time critical path.
++ * Usually a module / driver should initialize the necessary 
++ * rs_control structure on module / driver init and release it
++ * on exit.
++ * The encoding puts the calculated syndrome into a given syndrome 
++ * buffer. 
++ * The decoding is a two step process. The first step calculates
++ * the syndrome over the received (data + syndrome) and calls the
++ * second stage, which does the decoding / error correction itself.
++ * Many hw encoders provide a syndrome calculation over the received
++ * data + syndrome and can call the second stage directly.
++ *
++ */
++
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/rslib.h>
++#include <linux/slab.h>
++#include <asm/semaphore.h>
++
++/* This list holds all currently allocated rs control structures */
++static LIST_HEAD (rslist);
++/* Protection for the list */
++static DECLARE_MUTEX(rslistlock);
++
++/** 
++ * rs_init - Initialize a Reed-Solomon codec
++ *
++ * @symsize:  symbol size, bits (1-8)
++ * @gfpoly:   Field generator polynomial coefficients
++ * @fcr:      first root of RS code generator polynomial, index form
++ * @prim:     primitive element to generate polynomial roots
++ * @nroots:   RS code generator polynomial degree (number of roots)
++ *
++ * Allocate a control structure and the polynom arrays for faster
++ * en/decoding. Fill the arrays according to the given parameters
++ */
++static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, 
++                                 int prim, int nroots)
++{
++      struct rs_control *rs;
++      int i, j, sr, root, iprim;
++
++      /* Allocate the control structure */
++      rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
++      if (rs == NULL)
++              return NULL;
++
++      INIT_LIST_HEAD(&rs->list);
++
++      rs->mm = symsize;
++      rs->nn = (1 << symsize) - 1;
++      rs->fcr = fcr;
++      rs->prim = prim;
++      rs->nroots = nroots;
++      rs->gfpoly = gfpoly;
++
++      /* Allocate the arrays */
++      rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++      if (rs->alpha_to == NULL)
++              goto errrs;
++
++      rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++      if (rs->index_of == NULL)
++              goto erralp;
++
++      rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
++      if(rs->genpoly == NULL)
++              goto erridx;
++
++      /* Generate Galois field lookup tables */
++      rs->index_of[0] = rs->nn;       /* log(zero) = -inf */
++      rs->alpha_to[rs->nn] = 0;       /* alpha**-inf = 0 */
++      sr = 1;
++      for (i = 0; i < rs->nn; i++) {
++              rs->index_of[sr] = i;
++              rs->alpha_to[i] = sr;
++              sr <<= 1;
++              if (sr & (1 << symsize))
++                      sr ^= gfpoly;
++              sr &= rs->nn;
++      }
++      /* If it's not primitive, exit */
++      if(sr != 1)
++              goto errpol;
++
++      /* Find prim-th root of 1, used in decoding */
++      for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
++      /* prim-th root of 1, index form */
++      rs->iprim = iprim / prim;
++
++      /* Form RS code generator polynomial from its roots */
++      rs->genpoly[0] = 1;
++      for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
++              rs->genpoly[i + 1] = 1;
++              /* Multiply rs->genpoly[] by  @**(root + x) */
++              for (j = i; j > 0; j--) {
++                      if (rs->genpoly[j] != 0) {
++                              rs->genpoly[j] = rs->genpoly[j -1] ^ 
++                                      rs->alpha_to[rs_modnn(rs, 
++                                      rs->index_of[rs->genpoly[j]] + root)];
++                      } else
++                              rs->genpoly[j] = rs->genpoly[j - 1];
++              }
++              /* rs->genpoly[0] can never be zero */
++              rs->genpoly[0] = 
++                      rs->alpha_to[rs_modnn(rs, 
++                              rs->index_of[rs->genpoly[0]] + root)];
++      }
++      /* convert rs->genpoly[] to index form for quicker encoding */
++      for (i = 0; i <= nroots; i++)
++              rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
++      return rs;
++
++      /* Error exit */
++errpol:
++      kfree(rs->genpoly);
++erridx:
++      kfree(rs->index_of);
++erralp:
++      kfree(rs->alpha_to);
++errrs:
++      kfree(rs);
++      return NULL;
++}
++
++
++/** 
++ *  free_rs - Free the rs control structure, if its not longer used
++ *
++ *  @rs:      the control structure which is not longer used by the
++ *            caller
++ */
++void free_rs(struct rs_control *rs)
++{
++      down(&rslistlock);
++      rs->users--;
++      if(!rs->users) {
++              list_del(&rs->list);
++              kfree(rs->alpha_to);
++              kfree(rs->index_of);
++              kfree(rs->genpoly);
++              kfree(rs);
++      }
++      up(&rslistlock);
++}
++
++/** 
++ * init_rs - Find a matching or allocate a new rs control structure
++ *
++ *  @symsize: the symbol size (number of bits)
++ *  @gfpoly:  the extended Galois field generator polynomial coefficients,
++ *            with the 0th coefficient in the low order bit. The polynomial
++ *            must be primitive;
++ *  @fcr:     the first consecutive root of the rs code generator polynomial 
++ *            in index form
++ *  @prim:    primitive element to generate polynomial roots
++ *  @nroots:  RS code generator polynomial degree (number of roots)
++ */
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
++                         int nroots)
++{
++      struct list_head        *tmp;
++      struct rs_control       *rs;
++
++      /* Sanity checks */
++      if (symsize < 1)
++              return NULL;
++      if (fcr < 0 || fcr >= (1<<symsize))
++              return NULL;
++      if (prim <= 0 || prim >= (1<<symsize))
++              return NULL;
++      if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
++              return NULL;
++      
++      down(&rslistlock);
++
++      /* Walk through the list and look for a matching entry */
++      list_for_each(tmp, &rslist) {
++              rs = list_entry(tmp, struct rs_control, list);
++              if (symsize != rs->mm)
++                      continue;
++              if (gfpoly != rs->gfpoly)
++                      continue;
++              if (fcr != rs->fcr)
++                      continue;       
++              if (prim != rs->prim)
++                      continue;       
++              if (nroots != rs->nroots)
++                      continue;
++              /* We have a matching one already */
++              rs->users++;
++              goto out;
++      }
++
++      /* Create a new one */
++      rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
++      if (rs) {
++              rs->users = 1;
++              list_add(&rs->list, &rslist);
++      }
++out:  
++      up(&rslistlock);
++      return rs;
++}
++
++#ifdef CONFIG_REED_SOLOMON_ENC8
++/** 
++ *  encode_rs8 - Calculate the parity for data values (8bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @len:     data length 
++ *  @par:     parity data, must be initialized by caller (usually all 0)
++ *  @invmsk:  invert data mask (will be xored on data)
++ *
++ *  The parity uses a uint16_t data type to enable
++ *  symbol size > 8. The calling code must take care of encoding of the
++ *  syndrome result for storage itself.
++ */
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, 
++             uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs8);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_DEC8
++/** 
++ *  decode_rs8 - Decode codeword (8bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @par:     received parity data field
++ *  @len:     data length
++ *  @s:               syndrome data field (if NULL, syndrome is calculated)
++ *  @no_eras: number of erasures
++ *  @eras_pos:        position of erasures, can be NULL
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
++ *  @corr:    buffer to store correction bitmask on eras_pos
++ *
++ *  The syndrome and parity uses a uint16_t data type to enable
++ *  symbol size > 8. The calling code must take care of decoding of the
++ *  syndrome result and the received parity before calling this code.
++ */
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
++             uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
++             uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs8);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_ENC16
++/**
++ *  encode_rs16 - Calculate the parity for data values (16bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @len:     data length 
++ *  @par:     parity data, must be initialized by caller (usually all 0)
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
++ *
++ *  Each field in the data array contains up to symbol size bits of valid data.
++ */
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, 
++      uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs16);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_DEC16
++/** 
++ *  decode_rs16 - Decode codeword (16bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @par:     received parity data field
++ *  @len:     data length
++ *  @s:               syndrome data field (if NULL, syndrome is calculated)
++ *  @no_eras: number of erasures
++ *  @eras_pos:        position of erasures, can be NULL
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!) 
++ *  @corr:    buffer to store correction bitmask on eras_pos
++ *
++ *  Each field in the data array contains up to symbol size bits of valid data.
++ */
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
++              uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs16);
++#endif
++
++EXPORT_SYMBOL_GPL(init_rs);
++EXPORT_SYMBOL_GPL(free_rs);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
++MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
++
+--- linux-2.4.21/mm/swap.c~swap-performance
++++ linux-2.4.21/mm/swap.c
+@@ -28,7 +28,7 @@
+ int page_cluster;
+ pager_daemon_t pager_daemon = {
+-      512,    /* base number for calculating the number of tries */
++      128,    /* base number for calculating the number of tries */
+       SWAP_CLUSTER_MAX,       /* minimum number of tries */
+       8,      /* do swap I/O in clusters of this size */
+ };
+--- linux-2.4.21/mm/vmalloc.c~vmalloc
++++ linux-2.4.21/mm/vmalloc.c
+@@ -183,6 +183,9 @@
+               return NULL;
+       size += PAGE_SIZE;
++#ifdef VMALLOC_ALIGN
++      size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
++#endif
+       if (!size) {
+               kfree (area);
+               return NULL;
+--- linux-2.4.21/net/core/wireless.c~linux-iw241_we16-6
++++ linux-2.4.21/net/core/wireless.c
+@@ -2,7 +2,7 @@
+  * This file implement the Wireless Extensions APIs.
+  *
+  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
++ * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved.
+  *
+  * (As all part of the Linux kernel, this file is GPL)
+  */
+@@ -43,6 +43,11 @@
+  *    o Turn on WE_STRICT_WRITE by default + kernel warning
+  *    o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
+  *    o Fix off-by-one in test (extra_size <= IFNAMSIZ)
++ *
++ * v6 - 9.01.03 - Jean II
++ *    o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
++ *    o Add enhanced spy support : iw_handler_set_thrspy() and event.
++ *    o Add WIRELESS_EXT version display in /proc/net/wireless
+  */
+ /***************************** INCLUDES *****************************/
+@@ -52,6 +57,7 @@
+ #include <linux/types.h>              /* off_t */
+ #include <linux/netdevice.h>          /* struct ifreq, dev_get_by_name() */
+ #include <linux/rtnetlink.h>          /* rtnetlink stuff */
++#include <linux/if_arp.h>             /* ARPHRD_ETHER */
+ #include <linux/wireless.h>           /* Pretty obvious */
+ #include <net/iw_handler.h>           /* New driver API */
+@@ -65,6 +71,7 @@
+ /* Debuging stuff */
+ #undef WE_IOCTL_DEBUG         /* Debug IOCTL API */
+ #undef WE_EVENT_DEBUG         /* Debug Event dispatcher */
++#undef WE_SPY_DEBUG           /* Debug enhanced spy support */
+ /* Options */
+ #define WE_EVENT_NETLINK      /* Propagate events using rtnetlink */
+@@ -72,7 +79,7 @@
+ /************************* GLOBAL VARIABLES *************************/
+ /*
+- * You should not use global variables, because or re-entrancy.
++ * You should not use global variables, because of re-entrancy.
+  * On our case, it's only const, so it's OK...
+  */
+ /*
+@@ -115,11 +122,11 @@
+       /* SIOCSIWSPY */
+       { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0},
+       /* SIOCGIWSPY */
+-      { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0},
+-      /* -- hole -- */
+-      { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+-      /* -- hole -- */
+-      { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
++      { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0},
++      /* SIOCSIWTHRSPY */
++      { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0},
++      /* SIOCGIWTHRSPY */
++      { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0},
+       /* SIOCSIWAP */
+       { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
+       /* SIOCGIWAP */
+@@ -377,9 +384,9 @@
+       struct net_device *     dev;
+       size = sprintf(buffer,
+-                     "Inter-| sta-|   Quality        |   Discarded packets               | Missed\n"
+-                     " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon\n"
+-                      );
++                     "Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE\n"
++                     " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | %d\n",
++                     WIRELESS_EXT);
+       
+       pos += size;
+       len += size;
+@@ -1024,3 +1031,252 @@
+       return;         /* Always success, I guess ;-) */
+ }
++
++/********************** ENHANCED IWSPY SUPPORT **********************/
++/*
++ * In the old days, the driver was handling spy support all by itself.
++ * Now, the driver can delegate this task to Wireless Extensions.
++ * It needs to use those standard spy iw_handler in struct iw_handler_def,
++ * push data to us via XXX and include struct iw_spy_data in its
++ * private part.
++ * One of the main advantage of centralising spy support here is that
++ * it becomes much easier to improve and extend it without having to touch
++ * the drivers. One example is the addition of the Spy-Threshold events.
++ * Note : IW_WIRELESS_SPY is defined in iw_handler.h
++ */
++
++/*------------------------------------------------------------------*/
++/*
++ * Standard Wireless Handler : set Spy List
++ */
++int iw_handler_set_spy(struct net_device *    dev,
++                     struct iw_request_info * info,
++                     union iwreq_data *       wrqu,
++                     char *                   extra)
++{
++#ifdef IW_WIRELESS_SPY
++      struct iw_spy_data *    spydata = (dev->priv +
++                                         dev->wireless_handlers->spy_offset);
++      struct sockaddr *       address = (struct sockaddr *) extra;
++
++      /* Disable spy collection while we copy the addresses.
++       * As we don't disable interrupts, we need to do this to avoid races.
++       * As we are the only writer, this is good enough. */
++      spydata->spy_number = 0;
++
++      /* Are there are addresses to copy? */
++      if(wrqu->data.length > 0) {
++              int i;
++
++              /* Copy addresses */
++              for(i = 0; i < wrqu->data.length; i++)
++                      memcpy(spydata->spy_address[i], address[i].sa_data,
++                             ETH_ALEN);
++              /* Reset stats */
++              memset(spydata->spy_stat, 0,
++                     sizeof(struct iw_quality) * IW_MAX_SPY);
++
++#ifdef WE_SPY_DEBUG
++              printk(KERN_DEBUG "iw_handler_set_spy() :  offset %ld, spydata %p, num %d\n", dev->wireless_handlers->spy_offset, spydata, wrqu->data.length);
++              for (i = 0; i < wrqu->data.length; i++)
++                      printk(KERN_DEBUG
++                             "%02X:%02X:%02X:%02X:%02X:%02X \n",
++                             spydata->spy_address[i][0],
++                             spydata->spy_address[i][1],
++                             spydata->spy_address[i][2],
++                             spydata->spy_address[i][3],
++                             spydata->spy_address[i][4],
++                             spydata->spy_address[i][5]);
++#endif        /* WE_SPY_DEBUG */
++      }
++      /* Enable addresses */
++      spydata->spy_number = wrqu->data.length;
++
++      return 0;
++#else /* IW_WIRELESS_SPY */
++      return -EOPNOTSUPP;
++#endif /* IW_WIRELESS_SPY */
++}
++
++/*------------------------------------------------------------------*/
++/*
++ * Standard Wireless Handler : get Spy List
++ */
++int iw_handler_get_spy(struct net_device *    dev,
++                     struct iw_request_info * info,
++                     union iwreq_data *       wrqu,
++                     char *                   extra)
++{
++#ifdef IW_WIRELESS_SPY
++      struct iw_spy_data *    spydata = (dev->priv +
++                                         dev->wireless_handlers->spy_offset);
++      struct sockaddr *       address = (struct sockaddr *) extra;
++      int                     i;
++
++      wrqu->data.length = spydata->spy_number;
++
++      /* Copy addresses. */
++      for(i = 0; i < spydata->spy_number; i++)        {
++              memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
++              address[i].sa_family = AF_UNIX;
++      }
++      /* Copy stats to the user buffer (just after). */
++      if(spydata->spy_number > 0)
++              memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
++                     spydata->spy_stat,
++                     sizeof(struct iw_quality) * spydata->spy_number);
++      /* Reset updated flags. */
++      for(i = 0; i < spydata->spy_number; i++)
++              spydata->spy_stat[i].updated = 0;
++      return 0;
++#else /* IW_WIRELESS_SPY */
++      return -EOPNOTSUPP;
++#endif /* IW_WIRELESS_SPY */
++}
++
++/*------------------------------------------------------------------*/
++/*
++ * Standard Wireless Handler : set spy threshold
++ */
++int iw_handler_set_thrspy(struct net_device * dev,
++                        struct iw_request_info *info,
++                        union iwreq_data *    wrqu,
++                        char *                extra)
++{
++#ifdef IW_WIRELESS_THRSPY
++      struct iw_spy_data *    spydata = (dev->priv +
++                                         dev->wireless_handlers->spy_offset);
++      struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
++
++      /* Just do it */
++      memcpy(&(spydata->spy_thr_low), &(threshold->low),
++             2 * sizeof(struct iw_quality));
++
++      /* Clear flag */
++      memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
++
++#ifdef WE_SPY_DEBUG
++      printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
++#endif        /* WE_SPY_DEBUG */
++
++      return 0;
++#else /* IW_WIRELESS_THRSPY */
++      return -EOPNOTSUPP;
++#endif /* IW_WIRELESS_THRSPY */
++}
++
++/*------------------------------------------------------------------*/
++/*
++ * Standard Wireless Handler : get spy threshold
++ */
++int iw_handler_get_thrspy(struct net_device * dev,
++                        struct iw_request_info *info,
++                        union iwreq_data *    wrqu,
++                        char *                extra)
++{
++#ifdef IW_WIRELESS_THRSPY
++      struct iw_spy_data *    spydata = (dev->priv +
++                                         dev->wireless_handlers->spy_offset);
++      struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
++
++      /* Just do it */
++      memcpy(&(threshold->low), &(spydata->spy_thr_low),
++             2 * sizeof(struct iw_quality));
++
++      return 0;
++#else /* IW_WIRELESS_THRSPY */
++      return -EOPNOTSUPP;
++#endif /* IW_WIRELESS_THRSPY */
++}
++
++#ifdef IW_WIRELESS_THRSPY
++/*------------------------------------------------------------------*/
++/*
++ * Prepare and send a Spy Threshold event
++ */
++static void iw_send_thrspy_event(struct net_device *  dev,
++                               struct iw_spy_data *   spydata,
++                               unsigned char *        address,
++                               struct iw_quality *    wstats)
++{
++      union iwreq_data        wrqu;
++      struct iw_thrspy        threshold;
++
++      /* Init */
++      wrqu.data.length = 1;
++      wrqu.data.flags = 0;
++      /* Copy address */
++      memcpy(threshold.addr.sa_data, address, ETH_ALEN);
++      threshold.addr.sa_family = ARPHRD_ETHER;
++      /* Copy stats */
++      memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
++      /* Copy also thresholds */
++      memcpy(&(threshold.low), &(spydata->spy_thr_low),
++             2 * sizeof(struct iw_quality));
++
++#ifdef WE_SPY_DEBUG
++      printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
++             threshold.addr.sa_data[0],
++             threshold.addr.sa_data[1],
++             threshold.addr.sa_data[2],
++             threshold.addr.sa_data[3],
++             threshold.addr.sa_data[4],
++             threshold.addr.sa_data[5], threshold.qual.level);
++#endif        /* WE_SPY_DEBUG */
++
++      /* Send event to user space */
++      wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
++}
++#endif /* IW_WIRELESS_THRSPY */
++
++/* ---------------------------------------------------------------- */
++/*
++ * Call for the driver to update the spy data.
++ * For now, the spy data is a simple array. As the size of the array is
++ * small, this is good enough. If we wanted to support larger number of
++ * spy addresses, we should use something more efficient...
++ */
++void wireless_spy_update(struct net_device *  dev,
++                       unsigned char *        address,
++                       struct iw_quality *    wstats)
++{
++#ifdef IW_WIRELESS_SPY
++      struct iw_spy_data *    spydata = (dev->priv +
++                                         dev->wireless_handlers->spy_offset);
++      int                     i;
++      int                     match = -1;
++
++#ifdef WE_SPY_DEBUG
++      printk(KERN_DEBUG "wireless_spy_update() :  offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
++#endif        /* WE_SPY_DEBUG */
++
++      /* Update all records that match */
++      for(i = 0; i < spydata->spy_number; i++)
++              if(!memcmp(address, spydata->spy_address[i], ETH_ALEN)) {
++                      memcpy(&(spydata->spy_stat[i]), wstats,
++                             sizeof(struct iw_quality));
++                      match = i;
++              }
++#ifdef IW_WIRELESS_THRSPY
++      /* Generate an event if we cross the spy threshold.
++       * To avoid event storms, we have a simple hysteresis : we generate
++       * event only when we go under the low threshold or above the
++       * high threshold. */
++      if(match >= 0) {
++              if(spydata->spy_thr_under[match]) {
++                      if(wstats->level > spydata->spy_thr_high.level) {
++                              spydata->spy_thr_under[match] = 0;
++                              iw_send_thrspy_event(dev, spydata,
++                                                   address, wstats);
++                      }
++              } else {
++                      if(wstats->level < spydata->spy_thr_low.level) {
++                              spydata->spy_thr_under[match] = 1;
++                              iw_send_thrspy_event(dev, spydata,
++                                                   address, wstats);
++                      }
++              }
++      }
++#endif /* IW_WIRELESS_THRSPY */
++#endif /* IW_WIRELESS_SPY */
++}
+--- linux-2.4.21/net/ipv4/ipconfig.c~net-dhcp-timeout
++++ linux-2.4.21/net/ipv4/ipconfig.c
+@@ -87,7 +87,7 @@
+ /* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */
+ #define CONF_OPEN_RETRIES     2       /* (Re)open devices twice */
+-#define CONF_SEND_RETRIES     6       /* Send six requests per open */
++#define CONF_SEND_RETRIES     4       /* Send six requests per open */
+ #define CONF_INTER_TIMEOUT    (HZ/2)  /* Inter-device timeout: 1/2 second */
+ #define CONF_BASE_TIMEOUT     (HZ*2)  /* Initial timeout: 2 seconds */
+ #define CONF_TIMEOUT_RANDOM   (HZ)    /* Maximum amount of randomization */
+@@ -1238,9 +1238,11 @@
+ #endif
+                       if (--retries) {
++#ifndef CONFIG_ARCH_RAMSES
+                               printk(KERN_ERR 
+                                      "IP-Config: Reopening network devices...\n");
+                               goto try_try_again;
++#endif
+                       }
+                       /* Oh, well.  At least we tried. */
+--- linux-2.4.21/net/netsyms.c~linux-iw241_we16-6
++++ linux-2.4.21/net/netsyms.c
+@@ -601,6 +601,11 @@
+ #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
+ #include <net/iw_handler.h>
+ EXPORT_SYMBOL(wireless_send_event);
++EXPORT_SYMBOL(iw_handler_set_spy);
++EXPORT_SYMBOL(iw_handler_get_spy);
++EXPORT_SYMBOL(iw_handler_set_thrspy);
++EXPORT_SYMBOL(iw_handler_get_thrspy);
++EXPORT_SYMBOL(wireless_spy_update);
+ #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
+ #endif  /* CONFIG_NET */
index 8f59854..695df70 100644 (file)
@@ -5,7 +5,7 @@ LICENSE = "GPL"
 KV = "2.4.21"
 RMKV = "2"
 PXAV = "1"
-PR = "r2"
+PR = "r3"
 
 SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-${KV}.tar.bz2 \
           ftp://ftp.arm.linux.org.uk/pub/armlinux/source/kernel-patches/v2.4/patch-${KV}-rmk${RMKV}.gz;patch=1 \