aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1
diff options
context:
space:
mode:
authorDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
committerDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
commit709c4d66e0b107ca606941b988bad717c0b45d9b (patch)
tree37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1
parentfa6cd5a3b993f16c27de4ff82b42684516d433ba (diff)
downloadopenembedded-709c4d66e0b107ca606941b988bad717c0b45d9b.tar.gz
rename packages/ to recipes/ per earlier agreement
See links below for more details: http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326 http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816 Signed-off-by: Denys Dmytriyenko <denis@denix.org> Acked-by: Mike Westerhof <mwester@dls.net> Acked-by: Philip Balister <philip@balister.org> Acked-by: Khem Raj <raj.khem@gmail.com> Acked-by: Marcin Juszkiewicz <hrw@openembedded.org> Acked-by: Koen Kooi <koen@openembedded.org> Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1')
-rw-r--r--recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/diff-2.4.21-rmk2-pxa1.gzbin0 -> 248024 bytes
-rw-r--r--recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch108070
2 files changed, 108070 insertions, 0 deletions
diff --git a/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/diff-2.4.21-rmk2-pxa1.gz b/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/diff-2.4.21-rmk2-pxa1.gz
new file mode 100644
index 0000000000..b11188efc1
--- /dev/null
+++ b/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/diff-2.4.21-rmk2-pxa1.gz
Binary files differ
diff --git a/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch b/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
new file mode 100644
index 0000000000..efeba8c284
--- /dev/null
+++ b/recipes/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
@@ -0,0 +1,108070 @@
+
+# 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
+# bluetooth.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
+#
+
+--- linux-2.4.21/CREDITS~bluetooth
++++ linux-2.4.21/CREDITS
+@@ -1348,7 +1348,11 @@
+ N: Marcel Holtmann
+ E: marcel@holtmann.org
+ W: http://www.holtmann.org
+-D: Author of the Linux Bluetooth Subsystem PC Card drivers
++D: Maintainer of the Linux Bluetooth Subsystem
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport protocol driver
++D: Author and maintainer of the Bluetooth HID protocol driver
++D: Various other Bluetooth related patches, cleanups and fixes
+ S: Germany
+
+ N: Rob W. W. Hooft
+@@ -2624,6 +2628,7 @@
+ N: Aristeu Sergio Rozanski Filho
+ E: aris@conectiva.com.br
+ D: Support for EtherExpress 10 ISA (i82595) in eepro driver
++D: User level driver support for input
+ S: Conectiva S.A.
+ S: R. Tocantins, 89 - Cristo Rei
+ S: 80050-430 - Curitiba - Paraná
+--- linux-2.4.21/Documentation/Configure.help~bluetooth
++++ linux-2.4.21/Documentation/Configure.help
+@@ -14071,6 +14071,15 @@
+ accessible under char device 13:64+ - /dev/input/eventX in a generic
+ way. This is the future ...
+
++CONFIG_INPUT_UINPUT
++ Say Y here if you want to support user level drivers for input
++ subsystem accessible under char device 10:223 - /dev/input/uinput.
++
++ This driver is also available as a module ( = code which can be
++ inserted in and removed from the running kernel whenever you want).
++ The module will be called uinput.o. If you want to compile it as a
++ module, say M here and read <file:Documentation/modules.txt>.
++
+ USB Scanner support
+ CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your computer's
+@@ -21670,21 +21679,21 @@
+
+ Linux Bluetooth subsystem consist of several layers:
+ BlueZ Core (HCI device and connection manager, scheduler)
+- HCI Device drivers (interface to the hardware)
+- L2CAP Module (L2CAP protocol)
+- SCO Module (SCO links)
+- RFCOMM Module (RFCOMM protocol)
+- BNEP Module (BNEP protocol)
++ HCI Device drivers (Interface to the hardware)
++ SCO Module (SCO audio links)
++ L2CAP Module (Logical Link Control and Adaptation Protocol)
++ RFCOMM Module (RFCOMM Protocol)
++ BNEP Module (Bluetooth Network Encapsulation Protocol)
++ CMTP Module (CAPI Message Transport Protocol)
++ HIDP Module (Human Interface Device Protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build BlueZ Core
+- layer.
++ Say Y here to compile Bluetooth support into the kernel or say M to
++ compile it as module (bluez.o).
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+ utilities like hciconfig and hcid. These utilities and updates to
+ Bluetooth kernel modules are provided in the BlueZ package.
+- For more information, see <http://bluez.sourceforge.net/>.
+-
+- If you want to compile BlueZ Core as module (bluez.o) say M here.
++ For more information, see <http://www.bluez.org/>.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -21697,7 +21706,7 @@
+
+ SCO links support
+ CONFIG_BLUEZ_SCO
+- SCO link provides voice transport over Bluetooth. SCO support is
++ SCO link provides voice transport over Bluetooth. SCO support is
+ required for voice applications like Headset and Audio.
+
+ Say Y here to compile SCO support into the kernel or say M to
+@@ -21705,7 +21714,7 @@
+
+ RFCOMM protocol support
+ CONFIG_BLUEZ_RFCOMM
+- RFCOMM provides connection oriented stream transport. RFCOMM
++ RFCOMM provides connection oriented stream transport. RFCOMM
+ support is required for Dialup Networking, OBEX and other Bluetooth
+ applications.
+
+@@ -21719,12 +21728,8 @@
+ BNEP protocol support
+ CONFIG_BLUEZ_BNEP
+ BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
+- emulation layer on top of Bluetooth. BNEP is required for Bluetooth
+- PAN (Personal Area Network).
+-
+- To use BNEP, you will need user-space utilities provided in the
+- BlueZ-PAN package.
+- For more information, see <http://bluez.sourceforge.net>.
++ emulation layer on top of Bluetooth. BNEP is required for
++ Bluetooth PAN (Personal Area Network).
+
+ Say Y here to compile BNEP support into the kernel or say M to
+ compile it as module (bnep.o).
+@@ -21737,6 +21742,24 @@
+ CONFIG_BLUEZ_BNEP_PROTO_FILTER
+ This option enables the protocol filter support for BNEP.
+
++CMTP protocol support
++CONFIG_BLUEZ_CMTP
++ CMTP (CAPI Message Transport Protocol) is a transport layer
++ for CAPI messages. CMTP is required for the Bluetooth Common
++ ISDN Access Profile.
++
++ Say Y here to compile CMTP support into the kernel or say M to
++ compile it as module (cmtp.o).
++
++HIDP protocol support
++CONFIG_BLUEZ_HIDP
++ HIDP (Human Interface Device Protocol) is a transport layer
++ for HID reports. HIDP is required for the Bluetooth Human
++ Interface Device Profile.
++
++ Say Y here to compile HIDP support into the kernel or say M to
++ compile it as module (hidp.o).
++
+ HCI UART driver
+ CONFIG_BLUEZ_HCIUART
+ Bluetooth HCI UART driver.
+@@ -21781,7 +21804,7 @@
+ kernel or say M to compile it as module (hci_usb.o).
+
+ HCI USB SCO (voice) support
+-CONFIG_BLUEZ_USB_SCO
++CONFIG_BLUEZ_HCIUSB_SCO
+ This option enables the SCO support in the HCI USB driver. You need this
+ to transmit voice data with your Bluetooth USB device. And your device
+ must also support sending SCO data over the HCI layer, because some of
+@@ -21789,14 +21812,6 @@
+
+ Say Y here to compile support for HCI SCO data.
+
+-HCI USB zero packet support
+-CONFIG_BLUEZ_USB_ZERO_PACKET
+- This option is provided only as a work around for buggy Bluetooth USB
+- devices. Do NOT enable it unless you know for sure that your device
+- requires zero packets.
+-
+- Most people should say N here.
+-
+ HCI VHCI Virtual HCI device driver
+ CONFIG_BLUEZ_HCIVHCI
+ Bluetooth Virtual HCI device driver.
+@@ -21805,6 +21820,16 @@
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci.o).
+
++HCI BFUSB device driver
++CONFIG_BLUEZ_HCIBFUSB
++ Bluetooth HCI BlueFRITZ! USB driver.
++ This driver provides support for Bluetooth USB devices with AVM
++ interface:
++ AVM BlueFRITZ! USB
++
++ Say Y here to compile support for HCI BFUSB devices into the
++ kernel or say M to compile it as module (bfusb.o).
++
+ HCI DTL1 (PC Card) device driver
+ CONFIG_BLUEZ_HCIDTL1
+ Bluetooth HCI DTL1 (PC Card) driver.
+@@ -21824,9 +21849,6 @@
+ 3Com Bluetooth Card (3CRWB6096)
+ HP Bluetooth Card
+
+- The HCI BT3C driver uses external firmware loader program provided in
+- the BlueFW package. For more information, see <http://bluez.sf.net>.
+-
+ Say Y here to compile support for HCI BT3C devices into the
+ kernel or say M to compile it as module (bt3c_cs.o).
+
+@@ -26815,6 +26837,12 @@
+
+ If unsure, say N.
+
++Hotplug firmware loading support (EXPERIMENTAL)
++CONFIG_FW_LOADER
++ This option is provided for the case where no in-kernel-tree modules require
++ hotplug firmware loading support, but a module built outside the kernel tree
++ does.
++
+ NatSemi SCx200 support
+ CONFIG_SCx200
+ This provides basic support for the National Semiconductor SCx200
+--- /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/Documentation/devices.txt~bluetooth
++++ linux-2.4.21/Documentation/devices.txt
+@@ -419,6 +419,7 @@
+ 220 = /dev/mptctl Message passing technology (MPT) control
+ 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver
+ 222 = /dev/mvista/hasi Montavista PICMG high availability
++ 223 = /dev/input/uinput User level driver support for input
+ 240-255 Reserved for local use
+
+ 11 char Raw keyboard device
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/README
+@@ -0,0 +1,58 @@
++
++ request_firmware() hotplug interface:
++ ------------------------------------
++ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
++
++ Why:
++ ---
++
++ Today, the most extended way to use firmware in the Linux kernel is linking
++ it statically in a header file. Which has political and technical issues:
++
++ 1) Some firmware is not legal to redistribute.
++ 2) The firmware occupies memory permanently, even though it often is just
++ used once.
++ 3) Some people, like the Debian crowd, don't consider some firmware free
++ enough and remove entire drivers (e.g.: keyspan).
++
++ about in-kernel persistence:
++ ---------------------------
++ Under some circumstances, as explained below, it would be interesting to keep
++ firmware images in non-swappable kernel memory or even in the kernel image
++ (probably within initramfs).
++
++ Note that this functionality has not been implemented.
++
++ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
++
++ - If the device that needs the firmware is needed to access the
++ filesystem. When upon some error the device has to be reset and the
++ firmware reloaded, it won't be possible to get it from userspace.
++ e.g.:
++ - A diskless client with a network card that needs firmware.
++ - The filesystem is stored in a disk behind an scsi device
++ that needs firmware.
++ - Replacing buggy DSDT/SSDT ACPI tables on boot.
++ Note: this would require the persistent objects to be included
++ within the kernel image, probably within initramfs.
++
++ And the same device can be needed to access the filesystem or not depending
++ on the setup, so I think that the choice on what firmware to make
++ persistent should be left to userspace.
++
++ - Why register_firmware()+__init can be useful:
++ - For boot devices needing firmware.
++ - To make the transition easier:
++ The firmware can be declared __init and register_firmware()
++ called on module_init. Then the firmware is warranted to be
++ there even if "firmware hotplug userspace" is not there yet or
++ it doesn't yet provide the needed firmware.
++ Once the firmware is widely available in userspace, it can be
++ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
++
++ In either case, if firmware hotplug support is there, it can move the
++ firmware out of kernel memory into the real filesystem for later
++ usage.
++
++ Note: If persistence is implemented on top of initramfs,
++ register_firmware() may not be appropriate.
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/firmware_sample_driver.c
+@@ -0,0 +1,121 @@
++/*
++ * firmware_sample_driver.c -
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Sample code on how to use request_firmware() from drivers.
++ *
++ * Note that register_firmware() is currently useless.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include "linux/firmware.h"
++
++#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++char __init inkernel_firmware[] = "let's say that this is firmware\n";
++#endif
++
++static char ghost_device[] = "ghost0";
++
++static void sample_firmware_load(char *firmware, int size)
++{
++ u8 buf[size+1];
++ memcpy(buf, firmware, size);
++ buf[size] = '\0';
++ printk("firmware_sample_driver: firmware: %s\n", buf);
++}
++
++static void sample_probe_default(void)
++{
++ /* uses the default method to get the firmware */
++ const struct firmware *fw_entry;
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware not available\n");
++ return;
++ }
++
++ sample_firmware_load(fw_entry->data, fw_entry->size);
++
++ release_firmware(fw_entry);
++
++ /* finish setting up the device */
++}
++static void sample_probe_specific(void)
++{
++ /* Uses some specific hotplug support to get the firmware from
++ * userspace directly into the hardware, or via some sysfs file */
++
++ /* NOTE: This currently doesn't work */
++
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware load failed\n");
++ return;
++ }
++
++ /* request_firmware blocks until userspace finished, so at
++ * this point the firmware should be already in the device */
++
++ /* finish setting up the device */
++}
++static void sample_probe_async_cont(const struct firmware *fw, void *context)
++{
++ if(!fw){
++ printk(KERN_ERR
++ "firmware_sample_driver: firmware load failed\n");
++ return;
++ }
++
++ printk("firmware_sample_driver: device pointer \"%s\"\n",
++ (char *)context);
++ sample_firmware_load(fw->data, fw->size);
++}
++static void sample_probe_async(void)
++{
++ /* Let's say that I can't sleep */
++ int error;
++ error = request_firmware_nowait (THIS_MODULE,
++ "sample_driver_fw", ghost_device,
++ "my device pointer",
++ sample_probe_async_cont);
++ if(error){
++ printk(KERN_ERR
++ "firmware_sample_driver:"
++ " request_firmware_nowait failed\n");
++ }
++}
++
++static int sample_init(void)
++{
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++ register_firmware("sample_driver_fw", inkernel_firmware,
++ sizeof(inkernel_firmware));
++#endif
++ /* since there is no real hardware insertion I just call the
++ * sample probe functions here */
++ sample_probe_specific();
++ sample_probe_default();
++ sample_probe_async();
++ return 0;
++}
++static void __exit sample_exit(void)
++{
++}
++
++module_init (sample_init);
++module_exit (sample_exit);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/hotplug-script
+@@ -0,0 +1,16 @@
++#!/bin/sh
++
++# Simple hotplug script sample:
++#
++# Both $DEVPATH and $FIRMWARE are already provided in the environment.
++
++HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
++
++echo 1 > /sysfs/$DEVPATH/loading
++cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
++echo 0 > /sysfs/$DEVPATH/loading
++
++# To cancel the load in case of error:
++#
++# echo -1 > /sysfs/$DEVPATH/loading
++#
+--- linux-2.4.21/MAINTAINERS~bluetooth
++++ linux-2.4.21/MAINTAINERS
+@@ -302,16 +302,88 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Marcel Holtmann
++M: marcel@holtmann.org
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
++L: bluez-devel@lists.sf.net
+ W: http://bluez.sf.net
++W: http://www.bluez.org
++W: http://www.holtmann.org/linux/bluetooth/
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (PC Card Drivers)
++BLUETOOTH RFCOMM LAYER
+ P: Marcel Holtmann
+ M: marcel@holtmann.org
+-W: http://www.holtmann.org/linux/bluetooth/
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HIDP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI BCM203X DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
+ S: Maintained
+
+ BONDING DRIVER
+--- 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,1177 @@
++#
++# 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_UINPUT=m
++# 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_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=m
++CONFIG_BLUEZ_L2CAP=m
++CONFIG_BLUEZ_SCO=m
++CONFIG_BLUEZ_RFCOMM=m
++CONFIG_BLUEZ_RFCOMM_TTY=y
++CONFIG_BLUEZ_BNEP=m
++CONFIG_BLUEZ_BNEP_MC_FILTER=y
++CONFIG_BLUEZ_BNEP_PROTO_FILTER=y
++CONFIG_BLUEZ_HIDP=m
++
++#
++# Bluetooth device drivers
++#
++CONFIG_BLUEZ_HCIUSB=m
++CONFIG_BLUEZ_HCIUSB_SCO=y
++CONFIG_BLUEZ_HCIUART=m
++CONFIG_BLUEZ_HCIUART_H4=y
++CONFIG_BLUEZ_HCIUART_BCSP=y
++# CONFIG_BLUEZ_HCIUART_BCSP_TXCRC is not set
++CONFIG_BLUEZ_HCIBFUSB=m
++CONFIG_BLUEZ_HCIDTL1=m
++CONFIG_BLUEZ_HCIBT3C=m
++CONFIG_BLUEZ_HCIBLUECARD=m
++CONFIG_BLUEZ_HCIBTUART=m
++CONFIG_BLUEZ_HCIVHCI=m
++
++#
++# 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
++CONFIG_FW_LOADER=m
+--- 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
++}
++
++
++
++#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>
++
++
++
++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/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Config.in
+@@ -7,8 +7,7 @@
+
+ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
+ if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
+- bool ' SCO (voice) support' CONFIG_BLUEZ_USB_SCO
+- bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET
++ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO
+ fi
+
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+@@ -18,6 +17,8 @@
+ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
+ fi
+
++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
++
+ dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+ dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
+--- linux-2.4.21/drivers/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Makefile
+@@ -14,6 +14,8 @@
+ uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o
+ uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o
+
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o
++
+ obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
+ obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
+ obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/Makefile.lib
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/bfusb.c
+@@ -0,0 +1,782 @@
++/*
++ *
++ * AVM BlueFRITZ! USB driver
++ *
++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.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
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++
++#include <linux/firmware.h>
++#include <linux/usb.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.1"
++
++static struct usb_device_id bfusb_table[] = {
++ /* AVM BlueFRITZ! USB */
++ { USB_DEVICE(0x057c, 0x2200) },
++
++ { } /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE(usb, bfusb_table);
++
++
++#define BFUSB_MAX_BLOCK_SIZE 256
++
++#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
++
++#define BFUSB_TX_PROCESS 1
++#define BFUSB_TX_WAKEUP 2
++
++#define BFUSB_MAX_BULK_TX 1
++#define BFUSB_MAX_BULK_RX 1
++
++struct bfusb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ unsigned int bulk_in_ep;
++ unsigned int bulk_out_ep;
++ unsigned int bulk_pkt_size;
++
++ rwlock_t lock;
++
++ struct sk_buff_head transmit_q;
++
++ struct sk_buff *reassembly;
++
++ atomic_t pending_tx;
++ struct sk_buff_head pending_q;
++ struct sk_buff_head completed_q;
++};
++
++struct bfusb_scb {
++ struct urb *urb;
++};
++
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
++
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb = NULL;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ skb = skb_dequeue(&bfusb->completed_q);
++ if (skb) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ kfree_skb(skb);
++ }
++
++ return urb;
++}
++
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ while ((skb = skb_dequeue(&bfusb->pending_q))) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ usb_unlink_urb(urb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++ }
++
++ while ((urb = bfusb_get_completed(bfusb)))
++ usb_free_urb(urb);
++}
++
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
++{
++ struct bfusb_scb *scb = (void *) skb->cb;
++ struct urb *urb = bfusb_get_completed(bfusb);
++ int err, pipe;
++
++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++ bfusb_tx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ scb->urb = urb;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk tx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ usb_free_urb(urb);
++ } else
++ atomic_inc(&bfusb->pending_tx);
++
++ return err;
++}
++
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++ return;
++ }
++
++ do {
++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++
++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
++ (skb = skb_dequeue(&bfusb->transmit_q))) {
++ if (bfusb_send_bulk(bfusb, skb) < 0) {
++ skb_queue_head(&bfusb->transmit_q, skb);
++ break;
++ }
++ }
++
++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
++
++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
++
++static void bfusb_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ atomic_dec(&bfusb->pending_tx);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
++
++ if (!urb->status)
++ bfusb->hdev.stat.byte_tx += skb->len;
++ else
++ bfusb->hdev.stat.err_tx++;
++
++ read_lock(&bfusb->lock);
++
++ skb_unlink(skb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
++{
++ struct bfusb_scb *scb;
++ struct sk_buff *skb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
++
++ BT_DBG("bfusb %p urb %p", bfusb, urb);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) bfusb;
++
++ scb = (struct bfusb_scb *) skb->cb;
++ scb->urb = urb;
++
++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++ bfusb_rx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ kfree_skb(skb);
++ usb_free_urb(urb);
++ }
++
++ return err;
++}
++
++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
++{
++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
++
++ if (hdr & 0x10) {
++ BT_ERR("%s error in block", bfusb->hdev.name);
++ if (bfusb->reassembly)
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ return -EIO;
++ }
++
++ if (hdr & 0x04) {
++ struct sk_buff *skb;
++ unsigned char pkt_type;
++ int pkt_len = 0;
++
++ if (bfusb->reassembly) {
++ BT_ERR("%s unexpected start block", bfusb->hdev.name);
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ if (len < 1) {
++ BT_ERR("%s no packet type found", bfusb->hdev.name);
++ return -EPROTO;
++ }
++
++ pkt_type = *data++; len--;
++
++ switch (pkt_type) {
++ case HCI_EVENT_PKT:
++ if (len >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *hdr = (hci_event_hdr *) data;
++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
++ } else {
++ BT_ERR("%s event block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (len >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *hdr = (hci_acl_hdr *) data;
++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
++ } else {
++ BT_ERR("%s data block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (len >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *hdr = (hci_sco_hdr *) data;
++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
++ } else {
++ BT_ERR("%s audio block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++ }
++
++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) &bfusb->hdev;
++ skb->pkt_type = pkt_type;
++
++ bfusb->reassembly = skb;
++ } else {
++ if (!bfusb->reassembly) {
++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
++ return -EIO;
++ }
++ }
++
++ if (len > 0)
++ memcpy(skb_put(bfusb->reassembly, len), data, len);
++
++ if (hdr & 0x08) {
++ hci_recv_frame(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ return 0;
++}
++
++static void bfusb_rx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++ unsigned char *buf = urb->transfer_buffer;
++ int count = urb->actual_length;
++ int err, hdr, len;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ read_lock(&bfusb->lock);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ goto unlock;
++
++ if (urb->status || !count)
++ goto resubmit;
++
++ bfusb->hdev.stat.byte_rx += count;
++
++ skb_put(skb, count);
++
++ while (count) {
++ hdr = buf[0] | (buf[1] << 8);
++
++ if (hdr & 0x4000) {
++ len = 0;
++ count -= 2;
++ buf += 2;
++ } else {
++ len = (buf[2] == 0) ? 256 : buf[2];
++ count -= 3;
++ buf += 3;
++ }
++
++ if (count < len) {
++ BT_ERR("%s block extends over URB buffer ranges",
++ bfusb->hdev.name);
++ }
++
++ if ((hdr & 0xe1) == 0xc1)
++ bfusb_recv_block(bfusb, hdr, buf, len);
++
++ count -= len;
++ buf += len;
++ }
++
++ skb_unlink(skb);
++ kfree_skb(skb);
++
++ bfusb_rx_submit(bfusb, urb);
++
++ read_unlock(&bfusb->lock);
++
++ return;
++
++resubmit:
++ urb->dev = bfusb->udev;
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk resubmit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ }
++
++unlock:
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_open(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++ int i, err;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ MOD_INC_USE_COUNT;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ err = bfusb_rx_submit(bfusb, NULL);
++ if (!err) {
++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
++ bfusb_rx_submit(bfusb, NULL);
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ skb_queue_purge(&bfusb->transmit_q);
++
++ return 0;
++}
++
++static int bfusb_close(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ bfusb_unlink_urbs(bfusb);
++ bfusb_flush(hdev);
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++static int bfusb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct bfusb *bfusb;
++ struct sk_buff *nskb;
++ unsigned char buf[3];
++ int sent = 0, size, count;
++
++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
++
++ if (!hdev) {
++ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ bfusb = (struct bfusb *) hdev->driver_data;
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++
++ count = skb->len;
++
++ /* Max HCI frame size seems to be 1511 + 1 */
++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new packet");
++ return -ENOMEM;
++ }
++
++ nskb->dev = (void *) bfusb;
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
++
++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
++ buf[1] = 0x00;
++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
++
++ memcpy(skb_put(nskb, 3), buf, 3);
++ memcpy(skb_put(nskb, size), skb->data + sent, size);
++
++ sent += size;
++ count -= size;
++ }
++
++ /* Don't send frame with multiple size of bulk max packet */
++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
++ buf[0] = 0xdd;
++ buf[1] = 0x00;
++ memcpy(skb_put(nskb, 2), buf, 2);
++ }
++
++ read_lock(&bfusb->lock);
++
++ skb_queue_tail(&bfusb->transmit_q, nskb);
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++static void bfusb_destruct(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
++{
++ unsigned char *buf;
++ int err, pipe, len, size, sent = 0;
++
++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
++
++ BT_INFO("BlueFRITZ! USB loading firmware");
++
++ if (usb_set_configuration(bfusb->udev, 1) < 0) {
++ BT_ERR("Can't change to loading configuration");
++ return -EBUSY;
++ }
++
++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++ if (!buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
++
++ memcpy(buf, firmware + sent, size);
++
++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
++ &len, BFUSB_BLOCK_TIMEOUT);
++
++ if (err || (len != size)) {
++ BT_ERR("Error in firmware loading");
++ goto error;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
++ BT_ERR("Error in null packet request");
++ goto error;
++ }
++
++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
++ BT_ERR("Can't change to running configuration");
++ goto error;
++ }
++
++ BT_INFO("BlueFRITZ! USB device ready");
++
++ kfree(buf);
++ return 0;
++
++error:
++ kfree(buf);
++
++ pipe = usb_sndctrlpipe(bfusb->udev, 0);
++
++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
++
++ return err;
++}
++
++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct usb_interface *iface;
++ struct usb_interface_descriptor *iface_desc;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct hci_dev *hdev;
++ struct bfusb *bfusb;
++
++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
++
++ /* Check number of endpoints */
++ iface = &udev->actconfig->interface[0];
++ iface_desc = &iface->altsetting[0];
++
++ if (iface_desc->bNumEndpoints < 2)
++ return NULL;
++
++ bulk_out_ep = &iface_desc->endpoint[0];
++ bulk_in_ep = &iface_desc->endpoint[1];
++
++ if (!bulk_out_ep || !bulk_in_ep) {
++ BT_ERR("Bulk endpoints not found");
++ goto done;
++ }
++
++ /* Initialize control structure and load firmware */
++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
++ BT_ERR("Can't allocate memory for control structure");
++ goto done;
++ }
++
++ memset(bfusb, 0, sizeof(struct bfusb));
++
++ bfusb->udev = udev;
++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress;
++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress;
++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
++
++ bfusb->lock = RW_LOCK_UNLOCKED;
++
++ bfusb->reassembly = NULL;
++
++ skb_queue_head_init(&bfusb->transmit_q);
++ skb_queue_head_init(&bfusb->pending_q);
++ skb_queue_head_init(&bfusb->completed_q);
++
++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
++
++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
++ BT_ERR("Firmware request failed");
++ goto error;
++ }
++
++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
++ BT_ERR("Firmware loading failed");
++ goto release;
++ }
++
++ release_firmware(firmware);
++
++ /* Initialize and register HCI device */
++ hdev = &bfusb->hdev;
++
++ hdev->type = HCI_USB;
++ hdev->driver_data = bfusb;
++
++ hdev->open = bfusb_open;
++ hdev->close = bfusb_close;
++ hdev->flush = bfusb_flush;
++ hdev->send = bfusb_send_frame;
++ hdev->destruct = bfusb_destruct;
++ hdev->ioctl = bfusb_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device");
++ goto error;
++ }
++
++ return bfusb;
++
++release:
++ release_firmware(firmware);
++
++error:
++ kfree(bfusb);
++
++done:
++ return NULL;
++}
++
++static void bfusb_disconnect(struct usb_device *udev, void *ptr)
++{
++ struct bfusb *bfusb = (struct bfusb *) ptr;
++ struct hci_dev *hdev = &bfusb->hdev;
++
++ BT_DBG("udev %p ptr %p", udev, ptr);
++
++ if (!hdev)
++ return;
++
++ bfusb_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
++}
++
++static struct usb_driver bfusb_driver = {
++ name: "bfusb",
++ probe: bfusb_probe,
++ disconnect: bfusb_disconnect,
++ id_table: bfusb_table,
++};
++
++static int __init bfusb_init(void)
++{
++ int err;
++
++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ if ((err = usb_register(&bfusb_driver)) < 0)
++ BT_ERR("Failed to register BlueFRITZ! USB driver");
++
++ return err;
++}
++
++static void __exit bfusb_cleanup(void)
++{
++ usb_deregister(&bfusb_driver);
++}
++
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/bluecard_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bluecard_cs.c
+@@ -803,6 +803,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+--- linux-2.4.21/drivers/bluetooth/bt3c_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bt3c_cs.c
+@@ -24,8 +24,6 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+-#define __KERNEL_SYSCALLS__
+-
+ #include <linux/kernel.h>
+ #include <linux/kmod.h>
+ #include <linux/init.h>
+@@ -48,6 +46,8 @@
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+
++#include <linux/firmware.h>
++
+ #include <pcmcia/version.h>
+ #include <pcmcia/cs_types.h>
+ #include <pcmcia/cs.h>
+@@ -485,78 +485,101 @@
+
+
+
+-/* ======================== User mode firmware loader ======================== */
++/* ======================== Card services HCI interaction ======================== */
+
+
+-#define FW_LOADER "/sbin/bluefw"
+-static int errno;
++static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
++{
++ char *ptr = (char *) firmware;
++ char b[9];
++ unsigned int iobase, size, addr, fcs, tmp;
++ int i, err = 0;
+
++ iobase = info->link.io.BasePort1;
+
+-static int bt3c_fw_loader_exec(void *dev)
+-{
+- char *argv[] = { FW_LOADER, "pccard", dev, NULL };
+- char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+- int err;
++ /* Reset */
+
+- err = exec_usermodehelper(FW_LOADER, argv, envp);
+- if (err)
+- printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev);
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++ bt3c_io_write(iobase, 0x8040, 0x0400);
+
+- return err;
+-}
++ udelay(1);
+
++ bt3c_io_write(iobase, 0x8040, 0x0404);
+
+-static int bt3c_firmware_load(bt3c_info_t *info)
+-{
+- sigset_t tmpsig;
+- char dev[16];
+- pid_t pid;
+- int result;
++ udelay(17);
+
+- /* Check if root fs is mounted */
+- if (!current->fs->root) {
+- printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
+- return -EPERM;
+- }
++ /* Load */
+
+- sprintf(dev, "%04x", info->link.io.BasePort1);
++ while (count) {
++ if (ptr[0] != 'S') {
++ printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++ err = -EFAULT;
++ goto error;
++ }
+
+- pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0);
+- if (pid < 0) {
+- printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid);
+- return pid;
+- }
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 2, 2);
++ size = simple_strtol(b, NULL, 16);
+
+- /* Block signals, everything but SIGKILL/SIGSTOP */
+- spin_lock_irq(&current->sigmask_lock);
+- tmpsig = current->blocked;
+- siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 4, 8);
++ addr = simple_strtol(b, NULL, 16);
+
+- result = waitpid(pid, NULL, __WCLONE);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + (size * 2) + 2, 2);
++ fcs = simple_strtol(b, NULL, 16);
+
+- /* Allow signals again */
+- spin_lock_irq(&current->sigmask_lock);
+- current->blocked = tmpsig;
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ memset(b, 0, sizeof(b));
++ for (tmp = 0, i = 0; i < size; i++) {
++ memcpy(b, ptr + (i * 2) + 2, 2);
++ tmp += simple_strtol(b, NULL, 16);
++ }
+
+- if (result != pid) {
+- printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
+- return -result;
++ if (((tmp + fcs) & 0xff) != 0xff) {
++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
++ err = -EILSEQ;
++ goto error;
++ }
++
++ if (ptr[1] == '3') {
++ bt3c_address(iobase, addr);
++
++ memset(b, 0, sizeof(b));
++ for (i = 0; i < (size - 4) / 2; i++) {
++ memcpy(b, ptr + (i * 4) + 12, 4);
++ tmp = simple_strtol(b, NULL, 16);
++ bt3c_put(iobase, tmp);
++ }
++ }
++
++ ptr += (size * 2) + 6;
++ count -= (size * 2) + 6;
+ }
+
+- return 0;
+-}
++ udelay(17);
+
++ /* Boot */
+
++ bt3c_address(iobase, 0x3000);
++ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+-/* ======================== Card services HCI interaction ======================== */
++error:
++ udelay(17);
++
++ /* Clear */
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++ bt3c_io_write(iobase, 0x7005, 0x0000);
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ return err;
++}
+
+
+ int bt3c_open(bt3c_info_t *info)
+ {
++ const struct firmware *firmware;
++ char device[16];
+ struct hci_dev *hdev;
+ int err;
+
+@@ -570,8 +593,22 @@
+
+ /* Load firmware */
+
+- if ((err = bt3c_firmware_load(info)) < 0)
++ snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
++
++ err = request_firmware(&firmware, "BT3CPCC.bin", device);
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
+ return err;
++ }
++
++ err = bt3c_load_firmware(info, firmware->data, firmware->size);
++
++ release_firmware(firmware);
++
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
++ return err;
++ }
+
+ /* Timeout before it is safe to send the first HCI packet */
+
+@@ -606,6 +643,9 @@
+ {
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ bt3c_hci_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+--- linux-2.4.21/drivers/bluetooth/btuart_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/btuart_cs.c
+@@ -556,6 +556,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ btuart_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/dtl1_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/dtl1_cs.c
+@@ -535,6 +535,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ dtl1_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/hci_bcsp.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_bcsp.c
+@@ -34,7 +34,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+@@ -635,7 +634,8 @@
+ struct sk_buff *skb;
+ unsigned long flags;
+
+- BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
++
+ spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+--- linux-2.4.21/drivers/bluetooth/hci_ldisc.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_ldisc.c
+@@ -33,7 +33,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+--- linux-2.4.21/drivers/bluetooth/hci_uart.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_uart.h
+@@ -35,11 +35,12 @@
+ #define HCIUARTGETPROTO _IOR('U', 201, int)
+
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO 3
++#define HCI_UART_MAX_PROTO 4
+
+ #define HCI_UART_H4 0
+ #define HCI_UART_BCSP 1
+-#define HCI_UART_NCSP 2
++#define HCI_UART_3WIRE 2
++#define HCI_UART_H4DS 3
+
+ #ifdef __KERNEL__
+ struct hci_uart;
+--- linux-2.4.21/drivers/bluetooth/hci_usb.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.c
+@@ -30,7 +30,7 @@
+ *
+ * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $
+ */
+-#define VERSION "2.4"
++#define VERSION "2.7"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -62,7 +62,7 @@
+ #define BT_DMP( A... )
+ #endif
+
+-#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
+ #undef USB_ZERO_PACKET
+ #define USB_ZERO_PACKET 0
+ #endif
+@@ -73,20 +73,39 @@
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+
+- /* Ericsson with non-standard id */
+- { USB_DEVICE(0x0bdb, 0x1002) },
++ /* AVM BlueFRITZ! USB v2.0 */
++ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
++ /* ALPS Modules with non-standard id */
++ { USB_DEVICE(0x044e, 0x3001) },
++ { USB_DEVICE(0x044e, 0x3002) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
+ { } /* Terminating entry */
+ };
+
+ MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+-static struct usb_device_id ignore_ids[] = {
++static struct usb_device_id blacklist_ids[] = {
+ /* Broadcom BCM2033 without firmware */
+- { USB_DEVICE(0x0a5c, 0x2033) },
++ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
++
++ /* Broadcom BCM2035 */
++ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
++
++ /* ISSC Bluetooth Adapter v3.1 */
++ { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
++
++ /* Digianswer device */
++ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
++
++ /* RTX Telecom based adapter with buggy SCO support */
++ { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
+
+ { } /* Terminating entry */
+ };
+@@ -133,6 +152,7 @@
+ return _urb_dequeue(__completed_q(husb, type));
+ }
+
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+ {
+ int offset = 0, i;
+@@ -152,6 +172,7 @@
+ }
+ urb->number_of_packets = i;
+ }
++#endif
+
+ static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+ {
+@@ -229,7 +250,7 @@
+ return err;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+ {
+ struct _urb *_urb;
+@@ -300,8 +321,10 @@
+ for (i = 0; i < HCI_MAX_BULK_RX; i++)
+ hci_usb_bulk_rx_submit(husb);
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- hci_usb_isoc_rx_submit(husb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (husb->isoc_iface)
++ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
++ hci_usb_isoc_rx_submit(husb);
+ #endif
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+@@ -426,7 +449,7 @@
+ } else
+ dr = (void *) _urb->urb.setup_packet;
+
+- dr->bRequestType = HCI_CTRL_REQ;
++ dr->bRequestType = husb->ctrl_req;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+@@ -467,7 +490,7 @@
+ return __tx_submit(husb, _urb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+@@ -518,10 +541,10 @@
+ skb_queue_head(q, skb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ /* Process SCO queue */
+ q = __transmit_q(husb, HCI_SCODATA_PKT);
+- if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) &&
++ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
+ (skb = skb_dequeue(q))) {
+ if (hci_usb_send_isoc(husb, skb) < 0)
+ skb_queue_head(q, skb);
+@@ -577,7 +600,7 @@
+ hdev->stat.acl_tx++;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+@@ -627,7 +650,7 @@
+ } else
+ return -EILSEQ;
+ break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ if (count >= HCI_SCO_HDR_SIZE) {
+ hci_sco_hdr *h = data;
+@@ -638,7 +661,7 @@
+ #endif
+ }
+ BT_DBG("new packet len %d", len);
+-
++
+ skb = bluez_skb_alloc(len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for the packet", husb->hdev.name);
+@@ -683,16 +706,16 @@
+ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
+ _urb->type, urb->status, count, urb->transfer_flags);
+
+- if (!test_bit(HCI_RUNNING, &hdev->flags))
+- return;
+-
+ read_lock(&husb->completion_lock);
+
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto unlock;
++
+ if (urb->status || !count)
+ goto resubmit;
+
+ if (_urb->type == HCI_SCODATA_PKT) {
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ int i;
+ for (i=0; i < urb->number_of_packets; i++) {
+ BT_DBG("desc %d status %d offset %d len %d", i,
+@@ -724,6 +747,8 @@
+ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+ _urb->type, err);
+ }
++
++unlock:
+ read_unlock(&husb->completion_lock);
+ }
+
+@@ -786,8 +811,14 @@
+
+ iface = &udev->actconfig->interface[0];
+
+- /* Check our black list */
+- if (usb_match_id(udev, iface, ignore_ids))
++ if (!id->driver_info) {
++ const struct usb_device_id *match;
++ match = usb_match_id(udev, iface, blacklist_ids);
++ if (match)
++ id = match;
++ }
++
++ if (id->driver_info & HCI_IGNORE)
+ return NULL;
+
+ /* Check number of endpoints */
+@@ -827,9 +858,9 @@
+ bulk_out_ep[i] = ep;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case USB_ENDPOINT_XFER_ISOC:
+- if (ep->wMaxPacketSize < size)
++ if (ep->wMaxPacketSize < size || a > 2)
+ break;
+ size = ep->wMaxPacketSize;
+
+@@ -853,8 +884,8 @@
+ goto done;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
+ BT_DBG("Isoc endpoints not found");
+ isoc_iface = NULL;
+ }
+@@ -872,7 +903,12 @@
+ husb->bulk_in_ep = bulk_in_ep[0];
+ husb->intr_in_ep = intr_in_ep[0];
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++ if (id->driver_info & HCI_DIGIANSWER)
++ husb->ctrl_req = HCI_DIGI_REQ;
++ else
++ husb->ctrl_req = HCI_CTRL_REQ;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ if (isoc_iface) {
+ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
+ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+@@ -906,6 +942,9 @@
+ hdev->send = hci_usb_send_frame;
+ hdev->destruct = hci_usb_destruct;
+
++ if (id->driver_info & HCI_RESET)
++ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
++
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ goto probe_error;
+@@ -968,6 +1007,6 @@
+ module_init(hci_usb_init);
+ module_exit(hci_usb_cleanup);
+
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+ MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
+ MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/hci_usb.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.h
+@@ -35,12 +35,21 @@
+ #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+
+ #define HCI_CTRL_REQ 0x20
++#define HCI_DIGI_REQ 0x40
++
++#define HCI_IGNORE 0x01
++#define HCI_RESET 0x02
++#define HCI_DIGIANSWER 0x04
++#define HCI_BROKEN_ISOC 0x08
+
+ #define HCI_MAX_IFACE_NUM 3
+
+ #define HCI_MAX_BULK_TX 4
+ #define HCI_MAX_BULK_RX 1
+
++#define HCI_MAX_ISOC_RX 2
++#define HCI_MAX_ISOC_TX 2
++
+ #define HCI_MAX_ISOC_FRAMES 10
+
+ struct _urb_queue {
+@@ -119,6 +128,8 @@
+ struct usb_endpoint_descriptor *isoc_out_ep;
+ struct usb_endpoint_descriptor *isoc_in_ep;
+
++ __u8 ctrl_req;
++
+ struct sk_buff_head transmit_q[4];
+ struct sk_buff *reassembly[4]; // Reassembly buffers
+
+--- 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,86 @@
+ { 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,
++ }, {
++ type: PORT_UNKNOWN,
++ baud_base: 115200,
++ }, {
++ type: PORT_UNKNOWN,
++ baud_base: 115200,
++ }, {
++ type: PORT_UNKNOWN,
++ baud_base: 115200,
++ }, {
++ type: PORT_UNKNOWN,
++ baud_base: 115200,
++ }
+ };
+
+ #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 +283,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 +309,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 +368,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 +385,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 +542,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 +607,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 +654,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 +679,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 +757,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 +766,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 +807,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 +814,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 +841,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 +904,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 +930,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 +958,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 +994,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 +1008,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 +1031,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 +1041,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 +1076,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 +1145,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 +1163,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 +1201,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 +1247,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 +1308,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 +1390,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 +1411,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 +1420,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 +1430,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 +1493,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 +1501,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 +1508,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 +1516,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 +1536,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 +1563,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 +1585,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 +1627,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 +1652,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 +1689,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 +1710,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 +1726,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 +1747,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 +1884,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 +1902,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 +1910,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 +1919,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 +1931,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 +1940,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 +2006,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 +2086,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 +2104,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 +2203,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 +2241,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 +2255,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 +2268,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 +2303,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 +2377,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 +2482,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 +2509,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 +2564,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 +2582,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 +2636,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,125 +2670,212 @@
+ */
+
+ /*
+- * This routine prints out the appropriate serial driver version
+- * number, and identifies which options were configured into this
+- * driver.
++ * The serial driver boot-time initialization code!
+ */
+-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
++static int __init rs_init(void)
++{
++ int i;
++ struct serial_state * state;
++
++ 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 (!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 (!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;
++ }
++ }
++ 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;
++ }
++ }
++ 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;
++ }
++ }
++ init_bh(SERIAL_BH, do_serial_bh);
++ init_timer(&serial_timer);
++ serial_timer.function = rs_timer;
++ mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
++
++ for (i = 0; i < NR_IRQS; i++) {
++ IRQ_ports[i] = 0;
++ IRQ_timeout[i] = 0;
++ }
++#ifdef CONFIG_SERIAL_CONSOLE
++ /*
++ * The interrupt of the serial console port
++ * can't be shared.
++ */
++ if (sercons.flags & CON_CONSDEV) {
++ for(i = 0; i < NR_PORTS; i++)
++ if (i != sercons.index &&
++ rs_table[i].irq == rs_table[sercons.index].irq)
++ rs_table[i].irq = 0;
++ }
+ #endif
+-#ifdef ENABLE_SERIAL_ACPI
+- " SERIAL_ACPI"
+-#define SERIAL_OPT
++ /* Initialize the tty_driver structure */
++
++ memset(&serial_driver, 0, sizeof(struct tty_driver));
++ serial_driver.magic = TTY_DRIVER_MAGIC;
++ serial_driver.driver_name = "serial";
++#if defined(CONFIG_DEVFS_FS)
++ serial_driver.name = "tts/%d";
++#else
++ serial_driver.name = "ttyS";
+ #endif
+-#ifdef SERIAL_OPT
+- " enabled\n";
++ serial_driver.major = TTY_MAJOR;
++ 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 =
++ 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;
++ serial_driver.termios = serial_termios;
++ serial_driver.termios_locked = serial_termios_locked;
++
++ serial_driver.open = rs_open;
++ serial_driver.close = rs_close;
++ serial_driver.write = rs_write;
++ serial_driver.put_char = rs_put_char;
++ serial_driver.flush_chars = rs_flush_chars;
++ serial_driver.write_room = rs_write_room;
++ serial_driver.chars_in_buffer = rs_chars_in_buffer;
++ serial_driver.flush_buffer = rs_flush_buffer;
++ serial_driver.ioctl = rs_ioctl;
++ serial_driver.throttle = rs_throttle;
++ serial_driver.unthrottle = rs_unthrottle;
++ serial_driver.set_termios = rs_set_termios;
++ serial_driver.stop = rs_stop;
++ serial_driver.start = rs_start;
++ serial_driver.hangup = rs_hangup;
++ serial_driver.break_ctl = rs_break;
++ serial_driver.send_xchar = rs_send_xchar;
++ serial_driver.wait_until_sent = rs_wait_until_sent;
++ serial_driver.read_proc = rs_read_proc;
++
++ /*
++ * The callout device is just like normal device except for
++ * major number and the subtype code.
++ */
++ callout_driver = serial_driver;
++#if defined(CONFIG_DEVFS_FS)
++ callout_driver.name = "cua/%d";
+ #else
+- " no serial options enabled\n";
++ callout_driver.name = "cua";
+ #endif
+-#undef SERIAL_OPT
++ callout_driver.major = TTYAUX_MAJOR;
++ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
++ callout_driver.read_proc = 0;
++ callout_driver.proc_entry = 0;
+
+-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);
++ if (tty_register_driver(&serial_driver))
++ panic("Couldn't register serial driver\n");
++ if (tty_register_driver(&callout_driver))
++ panic("Couldn't register callout driver\n");
++
++ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
++ state->magic = SSTATE_MAGIC;
++ state->line = i;
++ state->custom_divisor = 0;
++ state->close_delay = 5*HZ/10;
++ state->closing_wait = 30*HZ;
++ state->callout_termios = callout_driver.init_termios;
++ state->normal_termios = serial_driver.init_termios;
++ state->icount.cts = state->icount.dsr =
++ state->icount.rng = state->icount.dcd = 0;
++ state->icount.rx = state->icount.tx = 0;
++ state->icount.frame = state->icount.parity = 0;
++ state->icount.overrun = state->icount.brk = 0;
++ state->irq = irq_cannonicalize(state->irq);
++ if (state->port && check_region(state->port,8)) {
++ state->type = PORT_UNKNOWN;
++ continue;
++ }
++ }
++ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
++ if (state->type == PORT_UNKNOWN)
++ continue;
++ 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);
++ }
++ return 0;
+ }
+
+ /*
+- * 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.
++ * This is for use by architectures that know their serial console
++ * attributes only at run time. Not to be invoked after rs_init().
+ */
+-static unsigned detect_uart_irq (struct serial_state * state)
++int __init early_serial_setup(struct serial_struct *req)
+ {
+- 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;
++ int i = req->line;
+
+- /* 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);
++ printk("%s\n", __FUNCTION__);
+
+- 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;
++ if (i >= NR_IRQS)
++ return(-ENOENT);
++ rs_table[i].magic = 0;
++ rs_table[i].baud_base = req->baud_base;
++ rs_table[i].port = req->port;
++ if (HIGH_BITS_OFFSET)
++ rs_table[i].port += (unsigned long) req->port_high <<
++ HIGH_BITS_OFFSET;
++ rs_table[i].irq = req->irq;
++ 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].iomem_base = req->iomem_base;
++ rs_table[i].iomem_reg_shift = req->iomem_reg_shift;
++ rs_table[i].type = req->type;
++ rs_table[i].xmit_fifo_size = req->xmit_fifo_size;
++ rs_table[i].custom_divisor = req->custom_divisor;
++ rs_table[i].closing_wait = req->closing_wait;
++ return(0);
+ }
+
+ /*
+@@ -3894,1762 +3228,10 @@
+ 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.
+- */
+-static int __devinit serial_pci_guess_board(struct pci_dev *dev,
+- struct pci_board *board)
+-{
+- int num_iomem = 0, num_port = 0, first_port = -1;
+- int i;
+-
+- /*
+- * 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++;
+- }
+-
+- /*
+- * 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 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;
+- }
+- /*
+- * 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++;
+- }
+- 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;
+- }
+-
+- 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;
+- mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
+-
+- 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
+- /*
+- * The interrupt of the serial console port
+- * can't be shared.
+- */
+- if (sercons.flags & CON_CONSDEV) {
+- for(i = 0; i < NR_PORTS; i++)
+- if (i != sercons.index &&
+- rs_table[i].irq == rs_table[sercons.index].irq)
+- 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))
+- 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.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;
+- serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+- serial_driver.refcount = &serial_refcount;
+- serial_driver.table = serial_table;
+- serial_driver.termios = serial_termios;
+- serial_driver.termios_locked = serial_termios_locked;
+-
+- serial_driver.open = rs_open;
+- serial_driver.close = rs_close;
+- serial_driver.write = rs_write;
+- serial_driver.put_char = rs_put_char;
+- serial_driver.flush_chars = rs_flush_chars;
+- serial_driver.write_room = rs_write_room;
+- serial_driver.chars_in_buffer = rs_chars_in_buffer;
+- serial_driver.flush_buffer = rs_flush_buffer;
+- serial_driver.ioctl = rs_ioctl;
+- serial_driver.throttle = rs_throttle;
+- serial_driver.unthrottle = rs_unthrottle;
+- serial_driver.set_termios = rs_set_termios;
+- 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))
+- 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");
+- if (tty_register_driver(&callout_driver))
+- panic("Couldn't register callout driver\n");
+-
+- for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+- state->magic = SSTATE_MAGIC;
+- state->line = i;
+- state->custom_divisor = 0;
+- state->close_delay = 5*HZ/10;
+- state->closing_wait = 30*HZ;
+- state->callout_termios = callout_driver.init_termios;
+- state->normal_termios = serial_driver.init_termios;
+- state->icount.cts = state->icount.dsr =
+- state->icount.rng = state->icount.dcd = 0;
+- state->icount.rx = state->icount.tx = 0;
+- 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,
+- 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;
+-}
+-
+-/*
+- * This is for use by architectures that know their serial console
+- * attributes only at run time. Not to be invoked after rs_init().
+- */
+-int __init early_serial_setup(struct serial_struct *req)
+-{
+- int i = req->line;
+-
+- if (i >= NR_IRQS)
+- return(-ENOENT);
+- rs_table[i].magic = 0;
+- rs_table[i].baud_base = req->baud_base;
+- rs_table[i].port = req->port;
+- if (HIGH_BITS_OFFSET)
+- rs_table[i].port += (unsigned long) req->port_high <<
+- HIGH_BITS_OFFSET;
+- rs_table[i].irq = req->irq;
+- 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;
+- rs_table[i].xmit_fifo_size = req->xmit_fifo_size;
+- rs_table[i].custom_divisor = req->custom_divisor;
+- rs_table[i].closing_wait = req->closing_wait;
+- return(0);
+-}
+-
+-/*
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+@@ -5734,11 +3316,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 +3325,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 +3364,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 +3381,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 +3514,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 +3522,8 @@
+ int quot = 0;
+ char *s;
+
++ printk("%s\n", __FUNCTION__);
++
+ if (options) {
+ baud = simple_strtoul(options, NULL, 10);
+ s = options;
+@@ -6028,19 +3598,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 +3645,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
+@@ -14,6 +16,7 @@
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
+ dep_tristate ' MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS
+
+ endmenu
+--- linux-2.4.21/drivers/input/Makefile~bluetooth
++++ 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,13 @@
+
+ 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_UINPUT) += uinput.o
+ obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o
++obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o
+
+ # The global Rules.make.
+
+--- linux-2.4.21/drivers/input/keybdev.c~bluetooth
++++ linux-2.4.21/drivers/input/keybdev.c
+@@ -154,16 +154,18 @@
+
+ static struct input_handler keybdev_handler;
+
++static unsigned int ledstate = 0xff;
++
+ void keybdev_ledfunc(unsigned int led)
+ {
+ struct input_handle *handle;
+
+- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
++ ledstate = led;
+
++ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+-
+ }
+ }
+
+@@ -203,6 +205,12 @@
+ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+ kbd_refresh_leds();
+
++ if (ledstate != 0xff) {
++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02));
++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04));
++ }
++
+ return handle;
+ }
+
+--- /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/uinput.c
+@@ -0,0 +1,428 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * 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
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/smp_lock.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/uinput.h>
++
++static int uinput_dev_open(struct input_dev *dev)
++{
++ return 0;
++}
++
++static void uinput_dev_close(struct input_dev *dev)
++{
++}
++
++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)dev->private;
++
++ udev->buff[udev->head].type = type;
++ udev->buff[udev->head].code = code;
++ udev->buff[udev->head].value = value;
++ do_gettimeofday(&udev->buff[udev->head].time);
++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
++
++ wake_up_interruptible(&udev->waitq);
++
++ return 0;
++}
++
++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
++{
++ return 0;
++}
++
++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
++{
++ return 0;
++}
++
++static int uinput_create_device(struct uinput_device *udev)
++{
++ if (!udev->dev->name) {
++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ udev->dev->open = uinput_dev_open;
++ udev->dev->close = uinput_dev_close;
++ udev->dev->event = uinput_dev_event;
++ udev->dev->upload_effect = uinput_dev_upload_effect;
++ udev->dev->erase_effect = uinput_dev_erase_effect;
++ udev->dev->private = udev;
++
++ init_waitqueue_head(&(udev->waitq));
++
++ input_register_device(udev->dev);
++
++ set_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_destroy_device(struct uinput_device *udev)
++{
++ if (!test_bit(UIST_CREATED, &(udev->state))) {
++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ input_unregister_device(udev->dev);
++
++ clear_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_open(struct inode *inode, struct file *file)
++{
++ struct uinput_device *newdev;
++ struct input_dev *newinput;
++
++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
++ if (!newdev)
++ goto error;
++ memset(newdev, 0, sizeof(struct uinput_device));
++
++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!newinput)
++ goto cleanup;
++ memset(newinput, 0, sizeof(struct input_dev));
++
++ newdev->dev = newinput;
++
++ file->private_data = newdev;
++
++ return 0;
++cleanup:
++ kfree(newdev);
++error:
++ return -ENOMEM;
++}
++
++static int uinput_validate_absbits(struct input_dev *dev)
++{
++ unsigned int cnt;
++ int retval = 0;
++
++ for (cnt = 0; cnt < ABS_MAX; cnt++) {
++ if (!test_bit(cnt, dev->absbit))
++ continue;
++
++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
++ (dev->absmax[cnt] <= dev->absmin[cnt])) {
++ printk(KERN_DEBUG
++ "%s: invalid abs[%02x] min:%d max:%d\n",
++ UINPUT_NAME, cnt,
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++
++ if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
++ (dev->absflat[cnt] > dev->absmax[cnt])) {
++ printk(KERN_DEBUG
++ "%s: absflat[%02x] out of range: %d "
++ "(min:%d/max:%d)\n",
++ UINPUT_NAME, cnt, dev->absflat[cnt],
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++ }
++ return retval;
++}
++
++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
++{
++ struct uinput_user_dev *user_dev;
++ struct input_dev *dev;
++ struct uinput_device *udev;
++ int size,
++ retval;
++
++ retval = count;
++
++ udev = (struct uinput_device *)file->private_data;
++ dev = udev->dev;
++
++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
++ if (!user_dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ if (NULL != dev->name)
++ kfree(dev->name);
++
++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
++ dev->name = kmalloc(size, GFP_KERNEL);
++ if (!dev->name) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ strncpy(dev->name, user_dev->name, size);
++ dev->idbus = user_dev->idbus;
++ dev->idvendor = user_dev->idvendor;
++ dev->idproduct = user_dev->idproduct;
++ dev->idversion = user_dev->idversion;
++ dev->ff_effects_max = user_dev->ff_effects_max;
++
++ size = sizeof(int) * (ABS_MAX + 1);
++ memcpy(dev->absmax, user_dev->absmax, size);
++ memcpy(dev->absmin, user_dev->absmin, size);
++ memcpy(dev->absfuzz, user_dev->absfuzz, size);
++ memcpy(dev->absflat, user_dev->absflat, size);
++
++ /* check if absmin/absmax/absfuzz/absflat are filled as
++ * told in Documentation/input/input-programming.txt */
++ if (test_bit(EV_ABS, dev->evbit)) {
++ retval = uinput_validate_absbits(dev);
++ if (retval < 0)
++ kfree(dev->name);
++ }
++
++exit:
++ kfree(user_dev);
++ return retval;
++}
++
++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++
++ if (test_bit(UIST_CREATED, &(udev->state))) {
++ struct input_event ev;
++
++ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
++ return -EFAULT;
++ input_event(udev->dev, ev.type, ev.code, ev.value);
++ }
++ else
++ count = uinput_alloc_device(file, buffer, count);
++
++ return count;
++}
++
++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++ int retval = 0;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
++ return -EAGAIN;
++
++ retval = wait_event_interruptible(udev->waitq,
++ (udev->head != udev->tail) ||
++ !test_bit(UIST_CREATED, &(udev->state)));
++
++ if (retval)
++ return retval;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ while ((udev->head != udev->tail) &&
++ (retval + sizeof(struct input_event) <= count)) {
++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
++ sizeof(struct input_event))) return -EFAULT;
++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
++ retval += sizeof(struct input_event);
++ }
++
++ return retval;
++}
++
++static unsigned int uinput_poll(struct file *file, poll_table *wait)
++{
++ struct uinput_device *udev = file->private_data;
++
++ poll_wait(file, &udev->waitq, wait);
++
++ if (udev->head != udev->tail)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++static int uinput_burn_device(struct uinput_device *udev)
++{
++ if (test_bit(UIST_CREATED, &(udev->state)))
++ uinput_destroy_device(udev);
++
++ kfree(udev->dev);
++ kfree(udev);
++
++ return 0;
++}
++
++static int uinput_close(struct inode *inode, struct file *file)
++{
++ return uinput_burn_device((struct uinput_device *)file->private_data);
++}
++
++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)file->private_data;
++
++ /* device attributes can not be changed after the device is created */
++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
++ return -EINVAL;
++
++ switch (cmd) {
++ case UI_DEV_CREATE:
++ retval = uinput_create_device(udev);
++ break;
++
++ case UI_DEV_DESTROY:
++ retval = uinput_destroy_device(udev);
++ break;
++
++ case UI_SET_EVBIT:
++ if (arg > EV_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->evbit);
++ break;
++
++ case UI_SET_KEYBIT:
++ if (arg > KEY_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->keybit);
++ break;
++
++ case UI_SET_RELBIT:
++ if (arg > REL_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->relbit);
++ break;
++
++ case UI_SET_ABSBIT:
++ if (arg > ABS_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->absbit);
++ break;
++
++ case UI_SET_MSCBIT:
++ if (arg > MSC_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->mscbit);
++ break;
++
++ case UI_SET_LEDBIT:
++ if (arg > LED_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ledbit);
++ break;
++
++ case UI_SET_SNDBIT:
++ if (arg > SND_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->sndbit);
++ break;
++
++ case UI_SET_FFBIT:
++ if (arg > FF_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ffbit);
++ break;
++
++ default:
++ retval = -EFAULT;
++ }
++ return retval;
++}
++
++struct file_operations uinput_fops = {
++ owner: THIS_MODULE,
++ open: uinput_open,
++ release: uinput_close,
++ read: uinput_read,
++ write: uinput_write,
++ poll: uinput_poll,
++ ioctl: uinput_ioctl,
++};
++
++static struct miscdevice uinput_misc = {
++ fops: &uinput_fops,
++ minor: UINPUT_MINOR,
++ name: UINPUT_NAME,
++};
++
++static int __init uinput_init(void)
++{
++ return misc_register(&uinput_misc);
++}
++
++static void __exit uinput_exit(void)
++{
++ misc_deregister(&uinput_misc);
++}
++
++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
++MODULE_DESCRIPTION("User level driver support for input subsystem");
++MODULE_LICENSE("GPL");
++
++module_init(uinput_init);
++module_exit(uinput_exit);
++
+--- /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/isdn/avmb1/capidrv.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/capidrv.c
+@@ -523,13 +523,25 @@
+
+ static void send_message(capidrv_contr * card, _cmsg * cmsg)
+ {
+- struct sk_buff *skb;
+- size_t len;
++ struct sk_buff *skb;
++ size_t len;
++ u16 err;
++
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
++ if(!skb) {
++ printk(KERN_ERR "no skb len(%d) memory\n", len);
++ return;
++ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+- (*capifuncs->capi_put_message) (global.appid, skb);
++ err = (*capifuncs->capi_put_message) (global.appid, skb);
++ if (err) {
++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
++ __FUNCTION__, err);
++ kfree_skb(skb);
++ return;
++ }
+ global.nsentctlpkt++;
+ }
+
+@@ -2188,10 +2200,10 @@
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+- if (card->plci_list)
+- printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
++ if (card->plci_list)
++ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ kfree(card->bchans);
+ card->bchans = 0;
+
+--- linux-2.4.21/drivers/isdn/avmb1/kcapi.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/kcapi.c
+@@ -546,7 +546,13 @@
+ static void notify_up(__u32 contr)
+ {
+ struct capi_interface_user *p;
++ __u16 appl;
+
++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
++ if (!VALID_APPLID(appl)) continue;
++ if (APPL(appl)->releasing) continue;
++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
++ }
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ spin_lock(&capi_users_lock);
+ for (p = capi_users; p; p = p->next) {
+@@ -714,12 +720,16 @@
+ nextpp = &(*pp)->next;
+ }
+ }
+- APPL(appl)->releasing--;
+- if (APPL(appl)->releasing <= 0) {
+- APPL(appl)->signal = 0;
+- APPL_MARK_FREE(appl);
+- printk(KERN_INFO "kcapi: appl %d down\n", appl);
+- }
++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */
++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
++ APPL(appl)->releasing--;
++ if (APPL(appl)->releasing <= 0) {
++ APPL(appl)->signal = 0;
++ APPL_MARK_FREE(appl);
++ printk(KERN_INFO "kcapi: appl %d down\n", appl);
++ }
++ } else
++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
+ }
+ /*
+ * ncci management
+@@ -872,16 +882,7 @@
+
+ static void controllercb_ready(struct capi_ctr * card)
+ {
+- __u16 appl;
+-
+ card->cardstate = CARD_RUNNING;
+-
+- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+- if (!VALID_APPLID(appl)) continue;
+- if (APPL(appl)->releasing) continue;
+- card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+- }
+-
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+
+--- 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: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $
++# $Id: Config.in,v 1.78 2004/08/09 18:46:03 dmarlin Exp $
+
+ 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: Makefile,v 1.65 2002/03/22 07:10:34 dwmw2 Exp $
+-
+-
+-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: Makefile.24,v 1.3 2004/08/11 14:45:53 dmarlin Exp $
+
+ # 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: Makefile.common,v 1.5 2004/08/10 20:51:49 dwmw2 Exp $
++
++# 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: afs.c,v 1.8 2002/05/04 08:49:09 rmk Exp $
++ $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $
+
+ ======================================================================*/
+
+@@ -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: Config.in,v 1.16 2002/09/03 13:30:43 joern Exp $
++# $Id: Config.in,v 1.20 2004/08/09 18:46:03 dmarlin Exp $
+
+ 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: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $
++# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $
+
+ 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: Makefile.common,v 1.4 2004/07/12 16:07:30 dwmw2 Exp $
++
++# *** 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: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $
++ * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $
+ *
+ * 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: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $
++ * $Id: cfi_cmdset_0001.c,v 1.168 2005/02/17 20:34:59 nico Exp $
+ *
+ *
+ * 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: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $
++ * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $
+ *
+ */
+
++#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: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $
+ *
+ * 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: cfi_probe.c,v 1.69 2002/05/11 22:13:03 dwmw2 Exp $
++ $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $
+ */
+
+ #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: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $
++ *
++ */
++
++#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: chipreg.c,v 1.13 2002/02/21 08:26:58 dwmw2 Exp $
++ * $Id: chipreg.c,v 1.18 2005/01/12 22:34:34 gleixner Exp $
+ *
+ * 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: gen_probe.c,v 1.9 2002/09/05 05:15:32 acurtis Exp $
++ * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $
+ */
+
+ #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: jedec.c,v 1.14 2002/06/27 02:19:12 dwmw2 Exp $
++ * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $
+ */
+
++#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: jedec_probe.c,v 1.19 2002/11/12 13:12:10 dwmw2 Exp $
++ $Id: jedec_probe.c,v 1.63 2005/02/14 16:30:32 bjd Exp $
+ 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: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $
++ * $Id: map_absent.c,v 1.5 2004/11/16 18:29:00 dwmw2 Exp $
+ *
+ * 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: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $
++ * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $
+ */
+
+ #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: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $
++ * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $
+ */
+
+-#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: sharp.c,v 1.8 2002/05/17 08:59:19 dwmw2 Exp $
++ * $Id: sharp.c,v 1.14 2004/08/09 13:19:43 dwmw2 Exp $
+ *
+ * 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: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $
++ * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
+ *
+ * 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: Config.in,v 1.8 2003/01/24 23:25:14 dwmw2 Exp $
++# $Id: Config.in,v 1.15 2004/10/01 21:47:13 gleixner Exp $
+
+ 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: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $
++# $Id: Makefile.24,v 1.2 2004/08/09 18:46:04 dmarlin Exp $
+
+ 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: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $
++
++# *** 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-24.c,v 1.23 2004/08/09 18:49:42 dmarlin Exp $
++ *
++ * 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: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $
++ * $Id: blkmtd.c,v 1.24 2004/11/16 18:29:01 dwmw2 Exp $
+ *
+ * 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: 1.24 $"
+
+ /* 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,v 1.25 2005/03/07 20:29:05 joern Exp $
++ *
++ * 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: 1.25 $"
++
++
++#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: doc2000.c,v 1.50 2002/12/10 15:05:42 gleixner Exp $
++ * $Id: doc2000.c,v 1.66 2005/01/05 18:05:12 dwmw2 Exp $
+ */
+
+ #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: doc2001.c,v 1.38 2002/12/10 15:05:42 gleixner Exp $
++ * $Id: doc2001.c,v 1.48 2005/01/05 18:05:12 dwmw2 Exp $
+ */
+
+ #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: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 Exp $
++ *
++ * 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: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $
++ * $Id: docecc.c,v 1.5 2003/05/21 15:15:06 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -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: docprobe.c,v 1.33 2003/01/24 14:02:47 dwmw2 Exp $ */
++/* $Id: docprobe.c,v 1.44 2005/01/05 12:40:36 dwmw2 Exp $ */
+
+
+
+@@ -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: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $
++ * $Id: lart.c,v 1.7 2004/08/09 13:19:44 dwmw2 Exp $
+ *
+ * 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: ms02-nv.c,v 1.2 2003/01/24 14:05:17 dwmw2 Exp $
++ * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $
+ */
+
+ #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: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $
+ */
+
+ #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: mtdram.c,v 1.29 2002/10/21 13:40:06 jocke Exp $
++ * $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $
+ * 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: phram.c,v 1.14 2005/03/07 21:43:38 joern Exp $
++ *
++ * 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: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $
++ * $Id: pmc551.c,v 1.30 2005/01/05 18:05:13 dwmw2 Exp $
+ *
+ * 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: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $
++ $Id: slram.c,v 1.34 2005/01/06 21:16:42 jwboyer Exp $
+
+ 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: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $
++ * $Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $
+ *
+ * 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: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\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: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $\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: inftlcore.c,v 1.18 2004/11/16 18:28:59 dwmw2 Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/config.h>
++#include <linux/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: 1.18 $, "
++ "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: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/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: 1.16 $";
++
++/*
++ * 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: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $
++# $Id: Config.in,v 1.72 2005/02/27 21:50:21 ppopov Exp $
+
+ 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: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $
++# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $
+
+ 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: Makefile.common,v 1.27 2005/03/07 23:15:48 joern Exp $
++
++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: amd76xrom.c,v 1.1 2002/10/18 22:45:48 eric Exp $
++ * $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $
+ */
+
+ #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: arctic-mtd.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $
++ *
++ * 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: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $
++ * $Id: autcpu12-nvram.c,v 1.8 2004/11/04 13:24:14 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -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: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/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: beech-mtd.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $
++ *
++ * 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: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $
++ * $Id: cdb89712.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $
+ */
+
+ #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: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $
++ * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $
+ */
+
+ #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: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $
++ * $Id: cfi_flagadm.c,v 1.14 2004/11/04 13:24:14 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+@@ -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: chestnut.c,v 1.1 2005/01/05 16:59:50 dwmw2 Exp $
++ *
++ * 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: cstm_mips_ixx.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: cstm_mips_ixx.c,v 1.13 2005/01/12 22:34:35 gleixner Exp $
+ *
+ * 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: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: dbox2-flash.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $
+ *
+- * 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: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $
++ * $Id: dc21285.c,v 1.22 2004/11/01 13:39:21 rmk Exp $
+ */
+ #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: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $
++ * $Id: dilnetpc.c,v 1.18 2005/01/12 22:34:35 gleixner Exp $
+ *
+ * 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: dmv182.c,v 1.5 2004/11/04 13:24:14 gleixner Exp $
++ *
++ * 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: ebony.c,v 1.15 2004/12/09 18:39:54 holindho Exp $
++ *
++ * 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: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
++ * $Id: edb7312.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $
+ *
+ * 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: epxa10db-flash.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/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: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $
++ * $Id: fortunet.c,v 1.9 2004/11/04 13:24:14 gleixner Exp $
+ */
+
+ #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: h720x-flash.c,v 1.11 2004/11/04 13:24:14 gleixner Exp $
++ *
++ * (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: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $
++ */
++
++#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: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
++ * $Id: impa7.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $
+ *
+ * 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: integrator-flash-v24.c,v 1.14 2004/09/16 23:27:13 gleixner Exp $
++
++======================================================================*/
++
++#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: integrator-flash.c,v 1.7 2001/11/01 20:55:47 rmk Exp $
++ $Id: integrator-flash.c,v 1.18 2004/11/01 13:26:15 rmk Exp $
+
+ ======================================================================*/
+
+@@ -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: ipaq-flash.c,v 1.4 2005/01/12 22:34:35 gleixner Exp $
++ */
++
++#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: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $
++ * $Id: iq80310.c,v 1.20 2004/11/04 13:24:15 gleixner Exp $
+ *
+ * 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: ixp2000.c,v 1.5 2004/11/16 17:15:48 dsaxena Exp $
++ *
++ * 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: ixp4xx.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $
++ *
++ * 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: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $
++ * $Id: l440gx.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $
+ *
+ * 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: lasat.c,v 1.1 2003/01/24 14:26:38 dwmw2 Exp $
++ * $Id: lasat.c,v 1.9 2004/11/04 13:24:15 gleixner Exp $
+ *
+ */
+
+ #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: lubbock-flash.c,v 1.19 2004/11/04 13:24:15 gleixner Exp $
++ *
++ * 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: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 Exp $
++ *
++ * 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: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $
++ * $Id: mbx860.c,v 1.8 2004/11/04 13:24:15 gleixner Exp $
+ *
+ * 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: mpc1211.c,v 1.4 2004/09/16 23:27:13 gleixner Exp $
++ *
++ * (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: mphysmap.c,v 1.2 2005/03/07 23:15:48 joern Exp $
++ *
++ * 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: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: netsc520.c,v 1.13 2004/11/28 09:40:40 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -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: nettel.c,v 1.1 2002/08/08 06:30:13 gerg Exp $
++ * $Id: nettel.c,v 1.10 2005/01/05 17:11:29 dwmw2 Exp $
+ */
+
+ /****************************************************************************/
+@@ -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: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: ocelot.c,v 1.16 2005/01/05 18:05:13 dwmw2 Exp $
+ *
+ * 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: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $
++// $Id: octagon-5066.c,v 1.27 2005/01/12 22:34:35 gleixner Exp $
+ /* ######################################################################
+
+ 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: omap-toto-flash.c,v 1.3 2004/09/16 23:27:13 gleixner Exp $
++ */
++
++#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: pci.c,v 1.2 2003/01/24 13:11:43 dwmw2 Exp $
++ * $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $
+ *
+ * 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: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $
++ * $Id: pcmciamtd.c,v 1.51 2004/07/12 22:38:29 dwmw2 Exp $
+ *
+ * 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: 1.39 $"
++#define DRIVER_VERSION "$Revision: 1.51 $"
+
+ /* 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: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $
++ * $Id: physmap.c,v 1.37 2004/11/28 09:40:40 dwmw2 Exp $
+ *
+ * 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: plat-ram.c,v 1.1 2005/01/24 00:37:02 bjd Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++#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: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: pnc2000.c,v 1.17 2004/11/16 18:29:02 dwmw2 Exp $
+ */
+
+ #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: redwood.c,v 1.10 2004/11/04 13:24:15 gleixner Exp $
+ *
+- * 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: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $
++ * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $
+ *
+ * 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: sa1100-flash.c,v 1.29 2002/09/06 14:36:19 abz Exp $
++ * $Id: sa1100-flash.c,v 1.47 2004/11/01 13:44:36 rmk Exp $
+ */
+
+ #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: sbc8240.c,v 1.4 2004/07/12 22:38:29 dwmw2 Exp $
++ *
++ */
++
++/*
++ * 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: sbc_gxx.c,v 1.21 2003/01/24 13:40:14 dwmw2 Exp $
++ $Id: sbc_gxx.c,v 1.34 2005/01/12 22:34:35 gleixner Exp $
+
+ 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: sc520cdp.c,v 1.11 2002/03/08 16:34:35 rkaiser Exp $
++ * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $
+ *
+ *
+ * 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: scb2_flash.c,v 1.2 2003/01/24 13:09:56 dwmw2 Exp $
++ * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $
+ * 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: scx200_docflash.c,v 1.1 2003/01/24 13:20:40 dwmw2 Exp $
++ $Id: scx200_docflash.c,v 1.10 2004/11/28 09:40:40 dwmw2 Exp $
+
+ 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: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $
++ *
++ * 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: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $
++ * $Id: solutionengine.c,v 1.14 2004/09/16 23:27:14 gleixner Exp $
+ *
+ * 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: sun_uflash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $
++/* $Id: sun_uflash.c,v 1.11 2004/11/04 13:24:15 gleixner Exp $
+ *
+ * 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: tqm8xxl.c,v 1.4 2002/06/20 13:41:20 mag Exp $
++ * $Id: tqm8xxl.c,v 1.13 2004/10/20 22:21:53 dwmw2 Exp $
+ *
+ * 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: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $
++ */
++
++#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: tsunami_flash.c,v 1.1 2002/01/10 22:59:13 eric Exp $
++ * $Id: tsunami_flash.c,v 1.9 2004/07/14 09:52:55 dwmw2 Exp $
+ */
+ #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: uclinux.c,v 1.2 2002/08/07 00:43:45 gerg Exp $
++ * $Id: uclinux.c,v 1.10 2005/01/05 18:05:13 dwmw2 Exp $
+ */
+
+ /****************************************************************************/
+@@ -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: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $
++// $Id: vmax301.c,v 1.31 2005/01/12 22:34:35 gleixner Exp $
+ /* ######################################################################
+
+ 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: walnut.c,v 1.2 2004/12/10 12:07:42 holindho Exp $
++ *
++ * 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: wr_sbc82xx_flash.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $
++ *
++ * 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: mtd_blkdevs-24.c,v 1.17 2005/01/05 17:35:22 dwmw2 Exp $
++ *
++ * (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: mtd_blkdevs.c,v 1.24 2004/11/16 18:28:59 dwmw2 Exp $
++ *
++ * (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: mtdblock.c,v 1.51 2001/11/20 11:42:33 dwmw2 Exp $
++ * $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $
+ *
+- * 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: mtdblock_ro.c,v 1.12 2001/11/20 11:42:33 dwmw2 Exp $
++ * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $
+ *
+- * 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: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $
++ * $Id: mtdchar.c,v 1.68 2005/02/08 19:12:50 nico Exp $
+ *
+ * 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: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $
++ * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $
+ */
+
+ #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: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $
++ * $Id: mtdcore.c,v 1.45 2005/02/18 14:34:50 dedekind Exp $
+ *
+ * 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: mtdpart.c,v 1.32 2002/10/21 13:40:05 jocke Exp $
++ * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $
+ *
+ * 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: Config.in,v 1.11 2002/12/01 13:23:05 gleixner Exp $
++# $Id: Config.in,v 1.24 2005/01/17 18:25:19 dmarlin Exp $
+
+ 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: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $
++# $Id: Makefile.24,v 1.1 2004/07/12 16:08:17 dwmw2 Exp $
+
+ 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: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $
++
++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: au1550nd.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $
++ *
++ * 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: autcpu12.c,v 1.6 2002/11/11 15:47:56 gleixner Exp $
++ * $Id: autcpu12.c,v 1.22 2004/11/04 12:53:10 gleixner Exp $
+ *
+ * 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: diskonchip.c,v 1.49 2005/02/22 21:48:21 gleixner Exp $
++ */
++
++#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: edb7312.c,v 1.3 2002/06/06 12:58:16 mag Exp $
++ * $Id: edb7312.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $
+ *
+ * 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: h1910.c,v 1.5 2004/11/04 12:53:10 gleixner Exp $
++ *
++ * 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: nand_base.c,v 1.135 2005/03/01 09:32:45 gleixner Exp $
++ *
++ * 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: nand_bbt.c,v 1.31 2005/02/16 17:09:36 dedekind Exp $
++ *
++ * 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: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $
++ * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
+ *
+- * 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: nand_ids.c,v 1.1 2002/12/02 22:06:04 gleixner Exp $
++ * $Id: nand_ids.c,v 1.12 2005/02/16 09:33:27 gleixner Exp $
+ *
+ * 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: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $
++ */
++
++#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: ppchameleonevb.c,v 1.6 2004/11/05 16:07:16 kalev Exp $
++ *
++ * 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: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin Exp $
++ *
++ * 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: s3c2410.c,v 1.8 2005/02/18 14:46:12 bjd Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++#include <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: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $
++ *
++ * 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: spia.c,v 1.16 2002/03/05 13:50:47 dwmw2 Exp $
++ * $Id: spia.c,v 1.24 2004/11/04 12:53:10 gleixner Exp $
+ *
+ * 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: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
++ */
++
++#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: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
++ *
++ * 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: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
++ *
++ * 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: nftlcore.c,v 1.87 2002/09/13 14:35:33 dwmw2 Exp $ */
++/* $Id: nftlcore.c,v 1.97 2004/11/16 18:28:59 dwmw2 Exp $ */
+
+ /*
+ 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: 1.87 $, 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: 1.97 $, 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: nftlmount.c,v 1.31 2002/11/15 16:34:43 dwmw2 Exp $
++ * $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -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: 1.31 $";
++char nftlmountrev[]="$Revision: 1.40 $";
+
+ /* 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: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $
++ * $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $
+ *
+ * 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/hid-core.c~bluetooth
++++ linux-2.4.21/drivers/usb/hid-core.c
+@@ -211,6 +211,8 @@
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
++ if (usages < parser->global.report_count)
++ usages = parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
+--- 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: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $
+-#
+-# 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: Makefile,v 1.43 2003/10/06 12:54:49 dwmw2 Exp $
+
++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: Makefile.common,v 1.9 2005/02/09 09:23:53 pavlov Exp $
++#
++
++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: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $
++ * $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
+ *
+ */
+
+-#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: build.c,v 1.16.2.3 2003/04/30 09:43:32 dwmw2 Exp $
++ * $Id: build.c,v 1.70 2005/02/28 08:21:05 dedekind Exp $
+ *
+ */
+
+ #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: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $
++ * $Id: compr.c,v 1.43 2005/01/12 22:34:35 gleixner Exp $
+ *
+ */
+
+-#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: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $
++ *
++ */
++
++#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: compr_lzari.c,v 1.3 2004/06/23 16:34:39 havasi Exp $
++ *
++ */
++
++/*
++ 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: compr_lzo.c,v 1.3 2004/06/23 16:34:39 havasi Exp $
++ *
++ */
++
++/*
++ 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: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $
++ * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $
+ *
+ *
+ * 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: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $
++ * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $
+ *
+ */
+
+
+ #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: compr_rubin.h,v 1.5 2001/02/26 13:50:01 dwmw2 Exp $ */
++/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */
+
+ #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: compr_zlib.c,v 1.8.2.1 2002/10/11 09:04:44 dwmw2 Exp $
++ * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
+ *
+ */
+
+-#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: crc32.c,v 1.3 2001/02/07 16:45:32 dwmw2 Exp $ */
++/* $Id: crc32.c,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+
+ #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: crc32.h,v 1.3 2001/02/26 14:44:37 dwmw2 Exp $ */
++/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+
+ #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: dir.c,v 1.45.2.7 2002/08/26 15:30:18 dwmw2 Exp $
++ * $Id: dir.c,v 1.85 2005/03/01 10:34:03 dedekind Exp $
+ *
+ */
+
+ #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: erase.c,v 1.24 2001/12/06 16:38:38 dwmw2 Exp $
++ * $Id: erase.c,v 1.72 2005/02/27 23:01:32 dwmw2 Exp $
+ *
+ */
++
+ #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: file.c,v 1.58.2.6 2002/11/12 13:17:01 dwmw2 Exp $
++ * $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
+ *
+ */
+
++#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: fs.c,v 1.53 2005/02/09 09:23:53 pavlov Exp $
++ *
++ */
++
++#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: gc.c,v 1.52.2.5 2002/10/10 13:18:38 dwmw2 Exp $
++ * $Id: gc.c,v 1.145 2005/02/09 09:09:01 pavlov Exp $
+ *
+ */
+
+ #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: ioctl.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $
++ * $Id: ioctl.c,v 1.9 2004/11/16 20:36:11 dwmw2 Exp $
+ *
+ */
+
+@@ -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: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $
++ * $Id: malloc.c,v 1.28 2004/11/16 20:36:11 dwmw2 Exp $
+ *
+ */
+
+@@ -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: nodelist.c,v 1.30.2.6 2003/02/24 21:49:33 dwmw2 Exp $
++ * $Id: nodelist.c,v 1.93 2005/02/27 23:01:32 dwmw2 Exp $
+ *
+ */
+
+ #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: nodelist.h,v 1.46.2.4 2003/02/24 21:49:33 dwmw2 Exp $
++ * $Id: nodelist.h,v 1.128 2005/02/27 23:01:32 dwmw2 Exp $
+ *
+ */
+
++#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: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $
++ * $Id: nodemgmt.c,v 1.119 2005/02/28 08:21:05 dedekind Exp $
+ *
+ */
+
+ #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: os-linux.h,v 1.54 2005/02/09 09:23:53 pavlov Exp $
++ *
++ */
++
++#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: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $
++ * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $
+ *
+ */
+
+ #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: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $
++*/
++
++#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: read.c,v 1.13.2.1 2002/02/01 23:32:33 dwmw2 Exp $
++ * $Id: read.c,v 1.39 2005/03/01 10:34:03 dedekind Exp $
+ *
+ */
+
+ #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: readinode.c,v 1.58.2.6 2002/10/10 13:18:38 dwmw2 Exp $
++ * $Id: readinode.c,v 1.119 2005/03/01 10:34:03 dedekind Exp $
+ *
+ */
+
+-/* 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: scan.c,v 1.51.2.3 2002/07/25 20:49:06 dwmw2 Exp $
++ * $Id: scan.c,v 1.119 2005/02/17 17:51:13 dedekind Exp $
+ *
+ */
+ #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: super-v24.c,v 1.83 2005/02/09 09:23:54 pavlov Exp $
++ *
++ */
++
++#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: super.c,v 1.48.2.3 2002/10/11 09:04:44 dwmw2 Exp $
++ * $Id: super.c,v 1.105 2005/02/09 09:23:54 pavlov Exp $
+ *
+ */
+
+ #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: symlink-v24.c,v 1.17 2005/03/04 13:12:25 dedekind Exp $
++ *
++ */
++
++
++#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: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $
++ * $Id: symlink.c,v 1.16 2005/03/01 10:50:48 dedekind Exp $
+ *
+ */
+
+@@ -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: wbuf.c,v 1.89 2005/02/09 09:23:54 pavlov Exp $
++ *
++ */
++
++#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: write.c,v 1.30.2.1 2002/08/08 08:36:31 dwmw2 Exp $
++ * $Id: write.c,v 1.91 2005/03/01 10:34:03 dedekind Exp $
+ *
+ */
+
+ #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: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $
++ *
++ */
++
++#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: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
++
++#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
+--- /dev/null
++++ linux-2.4.21/include/linux/firmware.h
+@@ -0,0 +1,20 @@
++#ifndef _LINUX_FIRMWARE_H
++#define _LINUX_FIRMWARE_H
++#include <linux/module.h>
++#include <linux/types.h>
++#define FIRMWARE_NAME_MAX 30
++struct firmware {
++ size_t size;
++ u8 *data;
++};
++int request_firmware (const struct firmware **fw, const char *name,
++ const char *device);
++int request_firmware_nowait (
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context));
++/* On 2.5 'device' is 'struct device *' */
++
++void release_firmware (const struct firmware *fw);
++void register_firmware (const char *name, const u8 *data, size_t size);
++#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/input.h~bluetooth
++++ linux-2.4.21/include/linux/input.h
+@@ -472,7 +472,8 @@
+ #define BUS_PCI 0x01
+ #define BUS_ISAPNP 0x02
+ #define BUS_USB 0x03
+-#define BUS_HIL 0x04
++#define BUS_HIL 0x04
++#define BUS_BLUETOOTH 0x05
+
+ #define BUS_ISA 0x10
+ #define BUS_I8042 0x11
+--- 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: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $
++ * $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $
+ *
+ */
+
+ #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: jffs2_fs_i.h,v 1.8 2001/04/18 13:05:28 dwmw2 Exp $ */
++/* $Id: jffs2_fs_i.h,v 1.17 2004/11/11 23:51:27 dwmw2 Exp $ */
+
+ #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: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */
++/* $Id: jffs2_fs_sb.h,v 1.51 2005/02/28 08:21:06 dedekind Exp $ */
+
+ #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: blktrans.h,v 1.5 2003/06/23 12:00:08 dwmw2 Exp $
++ *
++ * (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: cfi.h,v 1.32 2002/09/05 05:15:32 acurtis Exp $
++ * $Id: cfi.h,v 1.52 2005/02/08 17:11:15 nico Exp $
+ */
+
+ #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: compatmac.h,v 1.45 2003/01/24 15:50:57 dwmw2 Exp $
++ * $Id: compatmac.h,v 1.71 2005/01/12 23:01:17 dmarlin Exp $
+ *
+ * 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: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */
++/*
++ * 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: doc2000.h,v 1.24 2005/01/05 12:40:38 dwmw2 Exp $
++ *
++ * 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: flashchip.h,v 1.8 2002/10/21 13:20:52 jocke Exp $
++ * $Id: flashchip.h,v 1.16 2005/02/08 17:11:15 nico Exp $
+ *
+ */
+
+@@ -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: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $
++ * $Id: gen_probe.h,v 1.3 2004/10/20 22:10:33 dwmw2 Exp $
+ */
+
+ #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: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown Exp $
++ */
++
++#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: jedec.h,v 1.2 2001/11/06 14:37:36 dwmw2 Exp $
++ * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $
+ */
+
+ #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: map.h,v 1.29 2002/10/21 13:20:52 jocke Exp $ */
++/* $Id: map.h,v 1.48 2005/02/16 15:54:59 nico Exp $ */
+
+ #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: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */
++/*
++ * $Id: mtd.h,v 1.57 2005/02/08 17:11:15 nico Exp $
++ *
++ * 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: nand.h,v 1.19 2002/12/02 21:48:17 gleixner Exp $
++ * $Id: nand.h,v 1.71 2005/02/09 12:12:59 gleixner Exp $
+ *
+ * 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: nand_ecc.h,v 1.1 2000/10/12 00:57:15 sjhill Exp $
++ * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $
+ *
+ * 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: nftl.h,v 1.11 2002/06/18 13:54:24 dwmw2 Exp $ */
++/*
++ * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
++ *
++ * (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: partitions.h,v 1.8 2002/03/08 16:34:36 rkaiser Exp $
++ * $Id: partitions.h,v 1.16 2004/11/16 18:34:40 dwmw2 Exp $
+ */
+
+ #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: physmap.h,v 1.3 2004/07/21 00:16:15 jwboyer Exp $
++ *
++ * 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: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $
++ *
++ * 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: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $
++ */
++
++#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__ */
+--- linux-2.4.21/include/linux/net.h~bluetooth
++++ linux-2.4.21/include/linux/net.h
+@@ -139,6 +139,7 @@
+ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+ extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
++extern struct socket *sockfd_lookup(int fd, int *err);
+
+ extern int net_ratelimit(void);
+ extern unsigned long net_random(void);
+--- /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: rbtree.h,v 1.3 2003/01/14 13:56:05 dwmw2 Exp $
++ */
+
+-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: rslib.h,v 1.3 2004/10/05 22:08:22 gleixner Exp $
++ *
++ * 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: suspend.h,v 1.1 2003/10/13 20:56:47 dwmw2 Exp $ */
++
++#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) */
+
+--- /dev/null
++++ linux-2.4.21/include/linux/uinput.h
+@@ -0,0 +1,79 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * 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
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#ifndef __UINPUT_H_
++#define __UINPUT_H_
++
++#ifdef __KERNEL__
++#define UINPUT_MINOR 223
++#define UINPUT_NAME "uinput"
++#define UINPUT_BUFFER_SIZE 16
++
++/* state flags => bit index for {set|clear|test}_bit ops */
++#define UIST_CREATED 0
++
++struct uinput_device {
++ struct input_dev *dev;
++ unsigned long state;
++ wait_queue_head_t waitq;
++ unsigned char ready,
++ head,
++ tail;
++ struct input_event buff[UINPUT_BUFFER_SIZE];
++};
++#endif /* __KERNEL__ */
++
++/* ioctl */
++#define UINPUT_IOCTL_BASE 'U'
++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++#ifndef NBITS
++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
++#endif /* NBITS */
++
++#define UINPUT_MAX_NAME_SIZE 80
++struct uinput_user_dev {
++ char name[UINPUT_MAX_NAME_SIZE];
++ unsigned short idbus;
++ unsigned short idvendor;
++ unsigned short idproduct;
++ unsigned short idversion;
++ int ff_effects_max;
++ int absmax[ABS_MAX + 1];
++ int absmin[ABS_MAX + 1];
++ int absfuzz[ABS_MAX + 1];
++ int absflat[ABS_MAX + 1];
++};
++#endif /* __UINPUT_H_ */
+--- 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: workqueue.h,v 1.1 2002/11/11 16:39:10 dwmw2 Exp $
++ */
++
++#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: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $
++ *
++ * 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-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
++ *
++ * 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: mtd-abi.h,v 1.10 2005/02/09 09:17:42 pavlov Exp $
++ *
++ * 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-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
++ *
++ * 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: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
++ *
++ * 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/bluetooth/bluetooth.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/bluetooth.h
+@@ -51,6 +51,8 @@
+ #define BTPROTO_SCO 2
+ #define BTPROTO_RFCOMM 3
+ #define BTPROTO_BNEP 4
++#define BTPROTO_CMTP 5
++#define BTPROTO_HIDP 6
+
+ #define SOL_HCI 0
+ #define SOL_L2CAP 6
+@@ -155,7 +157,7 @@
+ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+ int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
+ uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
+-int bluez_sock_w4_connect(struct sock *sk, int flags);
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
+
+ void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
+ struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
+--- linux-2.4.21/include/net/bluetooth/hci.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci.h
+@@ -50,6 +50,11 @@
+ #define HCI_RS232 4
+ #define HCI_PCI 5
+
++/* HCI device quirks */
++enum {
++ HCI_QUIRK_RESET_ON_INIT
++};
++
+ /* HCI device flags */
+ enum {
+ HCI_UP,
+@@ -160,6 +165,7 @@
+ #define HCI_LM_AUTH 0x0002
+ #define HCI_LM_ENCRYPT 0x0004
+ #define HCI_LM_TRUSTED 0x0008
++#define HCI_LM_RELIABLE 0x0010
+
+ /* ----- HCI Commands ----- */
+ /* OGF & OCF values */
+@@ -333,6 +339,8 @@
+ } __attribute__ ((packed)) status_bdaddr_rp;
+ #define STATUS_BDADDR_RP_SIZE 7
+
++#define OCF_INQUIRY_CANCEL 0x0002
++
+ #define OCF_LINK_KEY_REPLY 0x000B
+ #define OCF_LINK_KEY_NEG_REPLY 0x000C
+ typedef struct {
+@@ -459,6 +467,17 @@
+ } __attribute__ ((packed)) inquiry_info;
+ #define INQUIRY_INFO_SIZE 14
+
++#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_period_mode;
++ __u8 dev_class[3];
++ __u16 clock_offset;
++ __s8 rssi;
++} __attribute__ ((packed)) inquiry_info_with_rssi;
++#define INQUIRY_INFO_WITH_RSSI_SIZE 14
++
+ #define EVT_CONN_COMPLETE 0x03
+ typedef struct {
+ __u8 status;
+--- linux-2.4.21/include/net/bluetooth/hci_core.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci_core.h
+@@ -72,7 +72,9 @@
+ __u16 pkt_type;
+ __u16 link_policy;
+ __u16 link_mode;
+-
++
++ unsigned long quirks;
++
+ atomic_t cmd_cnt;
+ unsigned int acl_cnt;
+ unsigned int sco_cnt;
+@@ -167,6 +169,12 @@
+ c->list = NULL;
+ }
+
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
++{
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return (c->list == NULL);
++}
++
+ static inline long inquiry_cache_age(struct hci_dev *hdev)
+ {
+ struct inquiry_cache *c = &hdev->inq_cache;
+@@ -282,10 +290,12 @@
+ static inline void hci_conn_put(struct hci_conn *conn)
+ {
+ if (atomic_dec_and_test(&conn->refcnt)) {
+- if (conn->type == SCO_LINK)
++ if (conn->type == ACL_LINK) {
++ unsigned long timeo = (conn->out) ?
++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
++ hci_conn_set_timer(conn, timeo);
++ } else
+ hci_conn_set_timer(conn, HZ / 100);
+- else if (conn->out)
+- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
+ }
+ }
+
+--- linux-2.4.21/include/net/bluetooth/l2cap.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/l2cap.h
+@@ -60,6 +60,7 @@
+ #define L2CAP_LM_AUTH 0x0002
+ #define L2CAP_LM_ENCRYPT 0x0004
+ #define L2CAP_LM_TRUSTED 0x0008
++#define L2CAP_LM_RELIABLE 0x0010
+
+ #define L2CAP_QOS 0x04
+ struct l2cap_qos {
+@@ -189,6 +190,14 @@
+ } __attribute__ ((packed)) l2cap_info_rsp;
+ #define L2CAP_INFO_RSP_SIZE 4
+
++/* info type */
++#define L2CAP_IT_CL_MTU 0x0001
++#define L2CAP_IT_FEAT_MASK 0x0002
++
++/* info result */
++#define L2CAP_IR_SUCCESS 0x0000
++#define L2CAP_IR_NOTSUPP 0x0001
++
+ /* ----- L2CAP connections ----- */
+ struct l2cap_chan_list {
+ struct sock *head;
+@@ -229,6 +238,7 @@
+ __u32 link_mode;
+
+ __u8 conf_state;
++ __u8 conf_retry;
+ __u16 conf_mtu;
+
+ __u8 ident;
+@@ -238,8 +248,11 @@
+ struct sock *prev_c;
+ };
+
+-#define CONF_REQ_SENT 0x01
+-#define CONF_INPUT_DONE 0x02
+-#define CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_REQ_SENT 0x01
++#define L2CAP_CONF_INPUT_DONE 0x02
++#define L2CAP_CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_MAX_RETRIES 2
++
++void l2cap_load(void);
+
+ #endif /* __L2CAP_H */
+--- linux-2.4.21/include/net/bluetooth/rfcomm.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/rfcomm.h
+@@ -167,8 +167,8 @@
+ int initiator;
+
+ /* Default DLC parameters */
++ int cfc;
+ uint mtu;
+- uint credits;
+
+ struct list_head dlcs;
+ };
+@@ -185,11 +185,12 @@
+ atomic_t refcnt;
+ u8 dlci;
+ u8 addr;
+-
+- uint mtu;
++ u8 priority;
+ u8 v24_sig;
++ u8 mscex;
+
+- uint credits;
++ uint mtu;
++ uint cfc;
+ uint rx_credits;
+ uint tx_credits;
+
+@@ -213,6 +214,16 @@
+ #define RFCOMM_SCHED_TIMEO 3
+ #define RFCOMM_SCHED_WAKEUP 31
+
++/* MSC exchange flags */
++#define RFCOMM_MSCEX_TX 1
++#define RFCOMM_MSCEX_RX 2
++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
++
++/* CFC states */
++#define RFCOMM_CFC_UNKNOWN -1
++#define RFCOMM_CFC_DISABLED 0
++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
++
+ extern struct task_struct *rfcomm_thread;
+ extern unsigned long rfcomm_event;
+
+--- 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~bluetooth
++++ linux-2.4.21/kernel/ksyms.c
+@@ -48,6 +48,7 @@
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
+ #include <linux/dnotify.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+
+ #if defined(CONFIG_PROC_FS)
+@@ -564,6 +565,13 @@
+ EXPORT_SYMBOL(strspn);
+ EXPORT_SYMBOL(strsep);
+
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
+ /* software interrupts */
+ EXPORT_SYMBOL(tasklet_hi_vec);
+ EXPORT_SYMBOL(tasklet_vec);
+@@ -585,6 +593,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,25 @@
+ 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
++
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++ "$CONFIG_HOTPLUG" = "y" ]; then
++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
++
+ endmenu
+--- linux-2.4.21/lib/Makefile~mtd-cvs
++++ linux-2.4.21/lib/Makefile
+@@ -8,11 +8,13 @@
+
+ L_TARGET := lib.a
+
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o rbtree.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++ rbtree.o firmware_class.o
+
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
+ bust_spinlocks.o rbtree.o dump_stack.o
+
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
+
+@@ -22,6 +24,9 @@
+
+ subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
+ subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
++subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
++
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
+
+ # Include the subdirs, if necessary.
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
+--- /dev/null
++++ linux-2.4.21/lib/firmware_class.c
+@@ -0,0 +1,573 @@
++/*
++ * firmware_class.c - Multi purpose firmware loading support
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
++ */
++/*
++ kernel/kmod.c
++ Kirk Petersen
++
++ Reorganized not to be a daemon by Adam Richter, with guidance
++ from Greg Zornetzer.
++
++ Modified to avoid chroot and file sharing problems.
++ Mikael Pettersson
++
++ Limit the concurrent number of kmod modprobes to catch loops from
++ "modprobe needs a service that is in a module".
++ Keith Owens <kaos@ocs.com.au> December 1999
++
++ Unblock all signals when we exec a usermode process.
++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
++*/
++/*
++ * drivers/usb/usb.c
++ *
++ * (C) Copyright Linus Torvalds 1999
++ * (C) Copyright Johannes Erdfelt 1999-2001
++ * (C) Copyright Andreas Gal 1999
++ * (C) Copyright Gregory P. Smith 1999
++ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
++ * (C) Copyright Randy Dunlap 2000
++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
++ * (C) Copyright Yggdrasil Computing, Inc. 2000
++ * (usb_device_id matching changes by Adam J. Richter)
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++#include <asm/hardirq.h>
++
++#include "linux/firmware.h"
++
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
++
++#define err(format, arg...) \
++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++
++static int loading_timeout = 10; /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
++
++#ifdef CONFIG_HOTPLUG
++
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++ char *argv[3], **envp, *buf, *scratch;
++ int i = 0;
++
++ int retval = 0;
++
++ if (!hotplug_path[0])
++ return -ENOENT;
++ if (in_interrupt()) {
++ err("in_interrupt");
++ return -EFAULT;
++ }
++ if (!current->fs->root) {
++ warn("call_policy %s -- no FS yet", verb);
++ return -EPERM;
++ }
++
++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
++ err("unable to allocate envp");
++ return -ENOMEM;
++ }
++ if (!(buf = kmalloc(256, GFP_KERNEL))) {
++ kfree(envp);
++ err("unable to allocate buf");
++ return -ENOMEM;
++ }
++
++ /* only one standardized param to hotplug command: type */
++ argv[0] = hotplug_path;
++ argv[1] = "firmware";
++ argv[2] = 0;
++
++ /* minimal command environment */
++ envp[i++] = "HOME=/";
++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++#ifdef DEBUG
++ /* hint that policy agent should enter no-stdout debug mode */
++ envp[i++] = "DEBUG=kernel";
++#endif
++ scratch = buf;
++
++ if (device) {
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
++ "DEVPATH=/driver/firmware/%s", device) + 1;
++ }
++
++ envp[i++] = scratch;
++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
++
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
++ "FIRMWARE=%s", name) + 1;
++
++ envp[i++] = 0;
++
++#ifdef DEBUG
++ dbg("firmware: %s %s %s", argv[0], argv[1], verb);
++#endif
++
++ retval = call_usermodehelper(argv[0], argv, envp);
++ if (retval) {
++ printk("call_usermodehelper return %d\n", retval);
++ }
++
++ kfree(buf);
++ kfree(envp);
++ return retval;
++}
++#else
++
++static inline int
++call_helper(char *verb, const char *name, const char *device)
++{
++ return -ENOENT;
++}
++
++#endif /* CONFIG_HOTPLUG */
++
++struct firmware_priv {
++ struct completion completion;
++ struct proc_dir_entry *proc_dir;
++ struct proc_dir_entry *attr_data;
++ struct proc_dir_entry *attr_loading;
++ struct firmware *fw;
++ int loading;
++ int abort;
++ int alloc_size;
++ struct timer_list timeout;
++};
++
++static int
++firmware_timeout_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ return sprintf(buf, "%d\n", loading_timeout);
++}
++
++/**
++ * firmware_timeout_store:
++ * Description:
++ * Sets the number of seconds to wait for the firmware. Once
++ * this expires an error will be return to the driver and no
++ * firmware will be provided.
++ *
++ * Note: zero means 'wait for ever'
++ *
++ **/
++static int
++firmware_timeout_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ loading_timeout = simple_strtol(buf, NULL, 10);
++ return count;
++}
++
++static int
++firmware_loading_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ return sprintf(buf, "%d\n", fw_priv->loading);
++}
++
++/**
++ * firmware_loading_store: - loading control file
++ * Description:
++ * The relevant values are:
++ *
++ * 1: Start a load, discarding any previous partial load.
++ * 0: Conclude the load and handle the data to the driver code.
++ * -1: Conclude the load with an error and discard any written data.
++ **/
++static int
++firmware_loading_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ int prev_loading = fw_priv->loading;
++
++ fw_priv->loading = simple_strtol(buf, NULL, 10);
++
++ switch (fw_priv->loading) {
++ case -1:
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++ break;
++ case 1:
++ kfree(fw_priv->fw->data);
++ fw_priv->fw->data = NULL;
++ fw_priv->fw->size = 0;
++ fw_priv->alloc_size = 0;
++ break;
++ case 0:
++ if (prev_loading == 1)
++ complete(&fw_priv->completion);
++ break;
++ }
++
++ return count;
++}
++
++static int
++firmware_data_read(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++
++ if (offset > fw->size)
++ return 0;
++ if (offset + count > fw->size)
++ count = fw->size - offset;
++
++ memcpy(buffer, fw->data + offset, count);
++ *start = (void *) ((long) count);
++ return count;
++}
++static int
++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
++{
++ u8 *new_data;
++ int new_size;
++
++ if (min_size <= fw_priv->alloc_size)
++ return 0;
++ if((min_size % PAGE_SIZE) == 0)
++ new_size = min_size;
++ else
++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
++ new_data = vmalloc(new_size);
++ if (!new_data) {
++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
++ /* Make sure that we don't keep incomplete data */
++ fw_priv->abort = 1;
++ return -ENOMEM;
++ }
++ fw_priv->alloc_size = new_size;
++ if (fw_priv->fw->data) {
++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
++ vfree(fw_priv->fw->data);
++ }
++ fw_priv->fw->data = new_data;
++ BUG_ON(min_size > fw_priv->alloc_size);
++ return 0;
++}
++
++/**
++ * firmware_data_write:
++ *
++ * Description:
++ *
++ * Data written to the 'data' attribute will be later handled to
++ * the driver as a firmware image.
++ **/
++static int
++firmware_data_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++ int offset = file->f_pos;
++ int retval;
++
++ retval = fw_realloc_buffer(fw_priv, offset + count);
++ if (retval) {
++ printk("%s: retval:%d\n", __FUNCTION__, retval);
++ return retval;
++ }
++
++ memcpy(fw->data + offset, buffer, count);
++
++ fw->size = max_t(size_t, offset + count, fw->size);
++ file->f_pos += count;
++ return count;
++}
++
++static void
++firmware_class_timeout(u_long data)
++{
++ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++}
++static int
++fw_setup_class_device(struct firmware_priv **fw_priv_p,
++ const char *fw_name, const char *device)
++{
++ int retval;
++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
++ GFP_KERNEL);
++ *fw_priv_p = fw_priv;
++ if (!fw_priv) {
++ retval = -ENOMEM;
++ goto out;
++ }
++ memset(fw_priv, 0, sizeof (*fw_priv));
++
++ init_completion(&fw_priv->completion);
++
++ fw_priv->timeout.function = firmware_class_timeout;
++ fw_priv->timeout.data = (u_long) fw_priv;
++ init_timer(&fw_priv->timeout);
++
++ retval = -EAGAIN;
++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
++ if (!fw_priv->proc_dir)
++ goto err_free_fw_priv;
++
++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_data)
++ goto err_remove_dir;
++
++ fw_priv->attr_data->read_proc = firmware_data_read;
++ fw_priv->attr_data->write_proc = firmware_data_write;
++ fw_priv->attr_data->data = fw_priv;
++
++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_loading)
++ goto err_remove_data;
++
++ fw_priv->attr_loading->read_proc = firmware_loading_show;
++ fw_priv->attr_loading->write_proc = firmware_loading_store;
++ fw_priv->attr_loading->data = fw_priv;
++
++ retval = 0;
++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
++ if (!fw_priv->fw) {
++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
++ __FUNCTION__);
++ retval = -ENOMEM;
++ goto err_remove_loading;
++ }
++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
++
++ goto out;
++
++err_remove_loading:
++ remove_proc_entry("loading", fw_priv->proc_dir);
++err_remove_data:
++ remove_proc_entry("data", fw_priv->proc_dir);
++err_remove_dir:
++ remove_proc_entry(device, proc_dir);
++err_free_fw_priv:
++ kfree(fw_priv);
++out:
++ return retval;
++}
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
++{
++ remove_proc_entry("loading", fw_priv->proc_dir);
++ remove_proc_entry("data", fw_priv->proc_dir);
++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
++}
++
++/**
++ * request_firmware: - request firmware to hotplug and wait for it
++ * Description:
++ * @firmware will be used to return a firmware image by the name
++ * of @name for device @device.
++ *
++ * Should be called from user context where sleeping is allowed.
++ *
++ * @name will be use as $FIRMWARE in the hotplug environment and
++ * should be distinctive enough not to be confused with any other
++ * firmware image for this or any other device.
++ **/
++int
++request_firmware(const struct firmware **firmware, const char *name,
++ const char *device)
++{
++ struct firmware_priv *fw_priv;
++ int retval;
++
++ if (!firmware) {
++ retval = -EINVAL;
++ goto out;
++ }
++ *firmware = NULL;
++
++ retval = fw_setup_class_device(&fw_priv, name, device);
++ if (retval)
++ goto out;
++
++ retval = call_helper("add", name, device);
++ if (retval)
++ goto out;
++ if (loading_timeout) {
++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
++ add_timer(&fw_priv->timeout);
++ }
++
++ wait_for_completion(&fw_priv->completion);
++
++ del_timer(&fw_priv->timeout);
++ fw_remove_class_device(fw_priv);
++
++ if (fw_priv->fw->size && !fw_priv->abort) {
++ *firmware = fw_priv->fw;
++ } else {
++ retval = -ENOENT;
++ vfree(fw_priv->fw->data);
++ kfree(fw_priv->fw);
++ }
++out:
++ kfree(fw_priv);
++ return retval;
++}
++
++void
++release_firmware(const struct firmware *fw)
++{
++ if (fw) {
++ vfree(fw->data);
++ kfree(fw);
++ }
++}
++
++/**
++ * register_firmware: - provide a firmware image for later usage
++ *
++ * Description:
++ * Make sure that @data will be available by requesting firmware @name.
++ *
++ * Note: This will not be possible until some kind of persistence
++ * is available.
++ **/
++void
++register_firmware(const char *name, const u8 *data, size_t size)
++{
++ /* This is meaningless without firmware caching, so until we
++ * decide if firmware caching is reasonable just leave it as a
++ * noop */
++}
++
++/* Async support */
++struct firmware_work {
++ struct tq_struct work;
++ struct module *module;
++ const char *name;
++ const char *device;
++ void *context;
++ void (*cont)(const struct firmware *fw, void *context);
++};
++
++static void
++request_firmware_work_func(void *arg)
++{
++ struct firmware_work *fw_work = arg;
++ const struct firmware *fw;
++ if (!arg)
++ return;
++ request_firmware(&fw, fw_work->name, fw_work->device);
++ fw_work->cont(fw, fw_work->context);
++ release_firmware(fw);
++ __MOD_DEC_USE_COUNT(fw_work->module);
++ kfree(fw_work);
++}
++
++/**
++ * request_firmware_nowait:
++ *
++ * Description:
++ * Asynchronous variant of request_firmware() for contexts where
++ * it is not possible to sleep.
++ *
++ * @cont will be called asynchronously when the firmware request is over.
++ *
++ * @context will be passed over to @cont.
++ *
++ * @fw may be %NULL if firmware request fails.
++ *
++ **/
++int
++request_firmware_nowait(
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context))
++{
++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
++ GFP_ATOMIC);
++ if (!fw_work)
++ return -ENOMEM;
++ if (!try_inc_mod_count(module)) {
++ kfree(fw_work);
++ return -EFAULT;
++ }
++
++ *fw_work = (struct firmware_work) {
++ .module = module,
++ .name = name,
++ .device = device,
++ .context = context,
++ .cont = cont,
++ };
++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
++
++ schedule_task(&fw_work->work);
++ return 0;
++}
++
++static int __init
++firmware_class_init(void)
++{
++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
++ if (!proc_dir)
++ return -EAGAIN;
++ proc_dir_timeout = create_proc_entry("timeout",
++ 0644 | S_IFREG, proc_dir);
++ if (!proc_dir_timeout) {
++ remove_proc_entry("driver/firmware", NULL);
++ return -EAGAIN;
++ }
++ proc_dir_timeout->read_proc = firmware_timeout_show;
++ proc_dir_timeout->write_proc = firmware_timeout_store;
++ return 0;
++}
++static void __exit
++firmware_class_exit(void)
++{
++ remove_proc_entry("timeout", proc_dir);
++ remove_proc_entry("driver/firmware", NULL);
++}
++
++module_init(firmware_class_init);
++module_exit(firmware_class_exit);
++
++#ifndef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
+--- /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: decode_rs.c,v 1.6 2004/10/22 15:41:47 gleixner Exp $
++ *
++ */
++
++/* 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: encode_rs.c,v 1.4 2004/10/22 15:41:47 gleixner Exp $
++ *
++ */
++
++/* 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: rslib.c,v 1.5 2004/10/22 15:41:47 gleixner Exp $
++ *
++ * 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/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/net/bluetooth/Config.in
+@@ -13,6 +13,8 @@
+ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
+ source net/bluetooth/rfcomm/Config.in
+ source net/bluetooth/bnep/Config.in
++ source net/bluetooth/cmtp/Config.in
++ source net/bluetooth/hidp/Config.in
+ source drivers/bluetooth/Config.in
+ fi
+
+--- linux-2.4.21/net/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/net/bluetooth/Makefile
+@@ -5,7 +5,7 @@
+ O_TARGET := bluetooth.o
+
+ list-multi := bluez.o
+-export-objs := syms.o
++export-objs := syms.o l2cap.o
+
+ bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
+
+@@ -15,6 +15,8 @@
+
+ subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
+ subdir-$(CONFIG_BLUEZ_BNEP) += bnep
++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
++subdir-$(CONFIG_BLUEZ_HIDP) += hidp
+
+ ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
+ obj-y += rfcomm/rfcomm.o
+@@ -24,6 +26,14 @@
+ obj-y += bnep/bnep.o
+ endif
+
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
+ include $(TOPDIR)/Rules.make
+
+ bluez.o: $(bluez-objs)
+--- linux-2.4.21/net/bluetooth/af_bluetooth.c~bluetooth
++++ linux-2.4.21/net/bluetooth/af_bluetooth.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $
+ */
+-#define VERSION "2.2"
++#define VERSION "2.4"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -57,7 +57,7 @@
+ #endif
+
+ /* Bluetooth sockets */
+-#define BLUEZ_MAX_PROTO 5
++#define BLUEZ_MAX_PROTO 7
+ static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+ int bluez_sock_register(int proto, struct net_proto_family *ops)
+@@ -221,12 +221,11 @@
+ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+ {
+ struct sock *sk = sock->sk;
+- unsigned int mask;
++ unsigned int mask = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ poll_wait(file, sk->sleep, wait);
+- mask = 0;
+
+ if (sk->err || !skb_queue_empty(&sk->error_queue))
+ mask |= POLLERR;
+@@ -242,9 +241,11 @@
+ if (sk->state == BT_CLOSED)
+ mask |= POLLHUP;
+
+- if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
++ if (sk->state == BT_CONNECT ||
++ sk->state == BT_CONNECT2 ||
++ sk->state == BT_CONFIG)
+ return mask;
+-
++
+ if (sock_writeable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+@@ -253,39 +254,35 @@
+ return mask;
+ }
+
+-int bluez_sock_w4_connect(struct sock *sk, int flags)
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+ int err = 0;
+
+ BT_DBG("sk %p", sk);
+
+ add_wait_queue(sk->sleep, &wait);
+- while (sk->state != BT_CONNECTED) {
++ while (sk->state != state) {
+ set_current_state(TASK_INTERRUPTIBLE);
++
+ if (!timeo) {
+ err = -EAGAIN;
+ break;
+ }
+
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+
+- err = 0;
+- if (sk->state == BT_CONNECTED)
+- break;
+-
+ if (sk->err) {
+ err = sock_error(sk);
+ break;
+ }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk->sleep, &wait);
+--- linux-2.4.21/net/bluetooth/bnep/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/core.c
+@@ -63,7 +63,7 @@
+ #define BT_DBG(D...)
+ #endif
+
+-#define VERSION "1.1"
++#define VERSION "1.2"
+
+ static LIST_HEAD(bnep_session_list);
+ static DECLARE_RWSEM(bnep_session_sem);
+@@ -113,13 +113,28 @@
+ return bnep_send(s, &rsp, sizeof(rsp));
+ }
+
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++static inline void bnep_set_default_proto_filter(struct bnep_session *s)
++{
++ /* (IPv4, ARP) */
++ s->proto_filter[0].start = htons(0x0800);
++ s->proto_filter[0].end = htons(0x0806);
++ /* (RARP, AppleTalk) */
++ s->proto_filter[1].start = htons(0x8035);
++ s->proto_filter[1].end = htons(0x80F3);
++ /* (IPX, IPv6) */
++ s->proto_filter[2].start = htons(0x8137);
++ s->proto_filter[2].end = htons(0x86DD);
++}
++#endif
++
+ static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
+ {
+ int n;
+
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned(data));
+ data++; len -= 2;
+
+@@ -141,9 +156,13 @@
+ BT_DBG("proto filter start %d end %d",
+ f[i].start, f[i].end);
+ }
++
+ if (i < BNEP_MAX_PROTO_FILTERS)
+ memset(f + i, 0, sizeof(*f));
+
++ if (n == 0)
++ bnep_set_default_proto_filter(s);
++
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
+ } else {
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
+@@ -160,7 +179,7 @@
+
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned((u16 *) data));
+ data += 2; len -= 2;
+
+@@ -214,7 +233,7 @@
+ int err = 0;
+
+ data++; len--;
+-
++
+ switch (cmd) {
+ case BNEP_CMD_NOT_UNDERSTOOD:
+ case BNEP_SETUP_CONN_REQ:
+@@ -268,13 +287,13 @@
+ /* Unknown extension, skip it. */
+ break;
+ }
+-
++
+ if (!skb_pull(skb, h->len)) {
+ err = -EILSEQ;
+ break;
+ }
+ } while (!err && (h->type & BNEP_EXT_HEADER));
+-
++
+ return err;
+ }
+
+@@ -300,7 +319,7 @@
+
+ if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+ goto badframe;
+-
++
+ if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
+ bnep_rx_control(s, skb->data, skb->len);
+ kfree_skb(skb);
+@@ -326,7 +345,7 @@
+ goto badframe;
+ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+ }
+-
++
+ /* We have to alloc new skb and copy data here :(. Because original skb
+ * may not be modified and because of the alignment requirements. */
+ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
+@@ -342,7 +361,7 @@
+ case BNEP_COMPRESSED:
+ memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
+ break;
+-
++
+ case BNEP_COMPRESSED_SRC_ONLY:
+ memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
+ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+@@ -362,7 +381,7 @@
+
+ memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+ kfree_skb(skb);
+-
++
+ s->stats.rx_packets++;
+ nskb->dev = dev;
+ nskb->ip_summed = CHECKSUM_UNNECESSARY;
+@@ -426,9 +445,9 @@
+ send:
+ iv[il++] = (struct iovec) { skb->data, skb->len };
+ len += skb->len;
+-
++
+ /* FIXME: linearize skb */
+-
++
+ s->msg.msg_iov = iv;
+ s->msg.msg_iovlen = il;
+ len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
+@@ -453,16 +472,16 @@
+
+ BT_DBG("");
+
+- daemonize(); reparent_to_init();
++ daemonize(); reparent_to_init();
+
+- sprintf(current->comm, "kbnepd %s", dev->name);
+-
+- sigfillset(&current->blocked);
++ sprintf(current->comm, "kbnepd %s", dev->name);
++
++ sigfillset(&current->blocked);
+ flush_signals(current);
+
+ current->nice = -15;
+
+- set_fs(KERNEL_DS);
++ set_fs(KERNEL_DS);
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(sk->sleep, &wait);
+@@ -477,13 +496,13 @@
+
+ if (sk->state != BT_CONNECTED)
+ break;
+-
++
+ // TX
+ while ((skb = skb_dequeue(&sk->write_queue)))
+ if (bnep_tx_frame(s, skb))
+ break;
+ netif_wake_queue(dev);
+-
++
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+@@ -547,28 +566,19 @@
+ s->sock = sock;
+ s->role = req->role;
+ s->state = BT_CONNECTED;
+-
++
+ s->msg.msg_flags = MSG_NOSIGNAL;
+
+ #ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+ /* Set default mc filter */
+ set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter);
+ #endif
+-
++
+ #ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+ /* Set default protocol filter */
+-
+- /* (IPv4, ARP) */
+- s->proto_filter[0].start = htons(0x0800);
+- s->proto_filter[0].end = htons(0x0806);
+- /* (RARP, AppleTalk) */
+- s->proto_filter[1].start = htons(0x8035);
+- s->proto_filter[1].end = htons(0x80F3);
+- /* (IPX, IPv6) */
+- s->proto_filter[2].start = htons(0x8137);
+- s->proto_filter[2].end = htons(0x86DD);
++ bnep_set_default_proto_filter(s);
+ #endif
+-
++
+ dev->init = bnep_net_init;
+ dev->priv = s;
+ err = register_netdev(dev);
+@@ -577,7 +587,7 @@
+ }
+
+ __bnep_link_session(s);
+-
++
+ err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (err < 0) {
+ /* Session thread start failed, gotta cleanup. */
+@@ -680,6 +690,8 @@
+
+ static int __init bnep_init_module(void)
+ {
++ l2cap_load();
++
+ bnep_crc32_init();
+ bnep_sock_init();
+
+--- linux-2.4.21/net/bluetooth/bnep/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/sock.c
+@@ -55,36 +55,6 @@
+ #define BT_DBG( A... )
+ #endif
+
+-static inline struct socket *socki_lookup(struct inode *inode)
+-{
+- return &inode->u.socket_i;
+-}
+-
+-static struct socket *sockfd_lookup(int fd, int *err)
+-{
+- struct file *file;
+- struct inode *inode;
+- struct socket *sock;
+-
+- if (!(file = fget(fd))) {
+- *err = -EBADF;
+- return NULL;
+- }
+-
+- inode = file->f_dentry->d_inode;
+- if (!inode->i_sock || !(sock = socki_lookup(inode))) {
+- *err = -ENOTSOCK;
+- fput(file);
+- return NULL;
+- }
+-
+- if (sock->file != file) {
+- printk(KERN_ERR "socki_lookup: socket file changed!\n");
+- sock->file = file;
+- }
+- return sock;
+-}
+-
+ static int bnep_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
+@@ -124,8 +94,10 @@
+ if (!nsock)
+ return err;
+
+- if (nsock->sk->state != BT_CONNECTED)
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
+ return -EBADFD;
++ }
+
+ err = bnep_add_connection(&ca, nsock);
+ if (!err) {
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Config.in
+@@ -0,0 +1,7 @@
++#
++# Bluetooth CMTP layer configuration
++#
++
++if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then
++ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
++fi
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth CMTP layer
++#
++
++O_TARGET := cmtp.o
++
++obj-y := core.o sock.o capi.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/capi.c
+@@ -0,0 +1,707 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <linux/capi.h>
++
++#include "../drivers/isdn/avmb1/capilli.h"
++#include "../drivers/isdn/avmb1/capicmd.h"
++#include "../drivers/isdn/avmb1/capiutil.h"
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define REVISION "1.0"
++
++#define CAPI_INTEROPERABILITY 0x20
++
++#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
++#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
++#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
++#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
++
++#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
++#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
++
++#define CAPI_FUNCTION_REGISTER 0
++#define CAPI_FUNCTION_RELEASE 1
++#define CAPI_FUNCTION_GET_PROFILE 2
++#define CAPI_FUNCTION_GET_MANUFACTURER 3
++#define CAPI_FUNCTION_GET_VERSION 4
++#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
++#define CAPI_FUNCTION_MANUFACTURER 6
++#define CAPI_FUNCTION_LOOPBACK 7
++
++static struct capi_driver_interface *di;
++
++
++#define CMTP_MSGNUM 1
++#define CMTP_APPLID 2
++#define CMTP_MAPPING 3
++
++static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
++{
++ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
++
++ BT_DBG("session %p application %p appl %d", session, app, appl);
++
++ if (!app)
++ return NULL;
++
++ memset(app, 0, sizeof(*app));
++
++ app->state = BT_OPEN;
++ app->appl = appl;
++
++ list_add_tail(&app->list, &session->applications);
++
++ return app;
++}
++
++static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
++{
++ BT_DBG("session %p application %p", session, app);
++
++ if (app) {
++ list_del(&app->list);
++ kfree(app);
++ }
++}
++
++static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
++{
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ switch (pattern) {
++ case CMTP_MSGNUM:
++ if (app->msgnum == value)
++ return app;
++ break;
++ case CMTP_APPLID:
++ if (app->appl == value)
++ return app;
++ break;
++ case CMTP_MAPPING:
++ if (app->mapping == value)
++ return app;
++ break;
++ }
++ }
++
++ return NULL;
++}
++
++static int cmtp_msgnum_get(struct cmtp_session *session)
++{
++ session->msgnum++;
++
++ if ((session->msgnum & 0xff) > 200)
++ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
++
++ return session->msgnum;
++}
++
++
++static void cmtp_send_interopmsg(struct cmtp_session *session,
++ __u8 subcmd, __u16 appl, __u16 msgnum,
++ __u16 function, unsigned char *buf, int len)
++{
++ struct sk_buff *skb;
++ unsigned char *s;
++
++ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
++
++ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for interoperability packet");
++ return;
++ }
++
++ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
++
++ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
++ capimsg_setu16(s, 2, appl);
++ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
++ capimsg_setu8 (s, 5, subcmd);
++ capimsg_setu16(s, 6, msgnum);
++
++ /* Interoperability selector (Bluetooth Device Management) */
++ capimsg_setu16(s, 8, 0x0001);
++
++ capimsg_setu8 (s, 10, 3 + len);
++ capimsg_setu16(s, 11, function);
++ capimsg_setu8 (s, 13, len);
++
++ if (len > 0)
++ memcpy(s + 14, buf, len);
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 appl, msgnum, func, info;
++ __u32 controller;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
++ case CAPI_CONF:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
++ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
++
++ switch (func) {
++ case CAPI_FUNCTION_REGISTER:
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
++ if (application) {
++ application->state = BT_CONNECTED;
++ application->msgnum = 0;
++ application->mapping = CAPIMSG_APPID(skb->data);
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_RELEASE:
++ appl = CAPIMSG_APPID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ application->state = BT_CLOSED;
++ application->msgnum = 0;
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_PROFILE:
++ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
++ session->ncontroller = controller;
++ wake_up_interruptible(&session->wait);
++ break;
++ }
++
++ if (!info && ctrl) {
++ memcpy(&ctrl->profile,
++ skb->data + CAPI_MSG_BASELEN + 11,
++ sizeof(capi_profile));
++ session->state = BT_CONNECTED;
++ ctrl->ready(ctrl);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_MANUFACTURER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
++
++ if (!info && ctrl) {
++ strncpy(ctrl->manu,
++ skb->data + CAPI_MSG_BASELEN + 15,
++ skb->data[CAPI_MSG_BASELEN + 14]);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_VERSION:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
++ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
++ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
++ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
++ strncpy(ctrl->serial,
++ skb->data + CAPI_MSG_BASELEN + 17,
++ skb->data[CAPI_MSG_BASELEN + 16]);
++ }
++
++ break;
++ }
++
++ break;
++
++ case CAPI_IND:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
++
++ if (func == CAPI_FUNCTION_LOOPBACK) {
++ appl = CAPIMSG_APPID(skb->data);
++ msgnum = CAPIMSG_MSGID(skb->data);
++ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
++ skb->data + CAPI_MSG_BASELEN + 6,
++ skb->data[CAPI_MSG_BASELEN + 5]);
++ }
++
++ break;
++ }
++
++ kfree_skb(skb);
++}
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 cmd, appl, info;
++ __u32 ncci, contr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
++ cmtp_recv_interopmsg(session, skb);
++ return;
++ }
++
++ if (session->flags & (1 << CMTP_LOOPBACK)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ appl = application->appl;
++ CAPIMSG_SETAPPID(skb->data, appl);
++ } else {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ if ((contr & 0x7f) == 0x01) {
++ contr = (contr & 0xffffff80) | session->num;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ if (!ctrl) {
++ BT_ERR("Can't find controller %d for message", session->num);
++ kfree_skb(skb);
++ return;
++ }
++
++ switch (cmd) {
++ case CAPI_CONNECT_B3_CONF:
++ ncci = CAPIMSG_NCCI(skb->data);
++ info = CAPIMSG_U16(skb->data, 12);
++
++ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
++
++ if (info == 0)
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_CONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
++
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_DISCONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
++
++ if (ncci == 0xffffffff)
++ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ ctrl->free_ncci(ctrl, appl, ncci);
++ break;
++
++ default:
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++ }
++}
++
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ scb->id = -1;
++ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
++
++ skb_queue_tail(&session->transmit, skb);
++
++ cmtp_schedule(session);
++}
++
++
++static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
++{
++ BT_DBG("ctrl %p data %p", ctrl, data);
++
++ return -EIO;
++}
++
++static void cmtp_reset_ctr(struct capi_ctr *ctrl)
++{
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->reseted(ctrl);
++}
++
++static void cmtp_remove_ctr(struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->suspend_output(ctrl);
++
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++}
++
++static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[8];
++ int err = 0, nconn, want = rp->level3cnt;
++
++ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
++ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
++
++ application = cmtp_application_add(session, appl);
++ if (!application) {
++ BT_ERR("Can't allocate memory for new application");
++ ctrl->appl_released(ctrl, appl);
++ return;
++ }
++
++ if (want < 0)
++ nconn = ctrl->profile.nbchannel * -want;
++ else
++ nconn = want;
++
++ if (nconn == 0)
++ nconn = ctrl->profile.nbchannel;
++
++ capimsg_setu16(buf, 0, nconn);
++ capimsg_setu16(buf, 2, rp->datablkcnt);
++ capimsg_setu16(buf, 4, rp->datablklen);
++
++ application->state = BT_CONFIG;
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
++ CAPI_FUNCTION_REGISTER, buf, 6);
++
++ add_wait_queue(&session->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (application->state == BT_CLOSED) {
++ err = -application->err;
++ break;
++ }
++
++ if (application->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ if (err) {
++ ctrl->appl_released(ctrl, appl);
++ cmtp_application_del(session, application);
++ return;
++ }
++
++ ctrl->appl_registered(ctrl, appl);
++}
++
++static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++
++ BT_DBG("ctrl %p appl %d", ctrl, appl);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if (!application) {
++ BT_ERR("Can't find application");
++ return;
++ }
++
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
++ CAPI_FUNCTION_RELEASE, NULL, 0);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (application->state == BT_CLOSED)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ cmtp_application_del(session, application);
++ ctrl->appl_released(ctrl, appl);
++}
++
++static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ __u16 appl;
++ __u32 contr;
++
++ BT_DBG("ctrl %p skb %p", ctrl, skb);
++
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if ((!application) || (application->state != BT_CONNECTED)) {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ CAPIMSG_SETAPPID(skb->data, application->mapping);
++
++ if ((contr & 0x7f) == session->num) {
++ contr = (contr & 0xffffff80) | 0x01;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static char *cmtp_procinfo(struct capi_ctr *ctrl)
++{
++ return "CAPI Message Transport Protocol";
++}
++
++static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++ int len = 0;
++
++ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
++ len += sprintf(page + len, "addr %s\n", session->name);
++ len += sprintf(page + len, "ctrl %d\n", session->num);
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
++ }
++
++ if (off + count >= len)
++ *eof = 1;
++
++ if (len < off)
++ return 0;
++
++ *start = page + off;
++
++ return ((count < len - off) ? count : len - off);
++}
++
++static struct capi_driver cmtp_driver = {
++ name: "cmtp",
++ revision: REVISION,
++ load_firmware: cmtp_load_firmware,
++ reset_ctr: cmtp_reset_ctr,
++ remove_ctr: cmtp_remove_ctr,
++ register_appl: cmtp_register_appl,
++ release_appl: cmtp_release_appl,
++ send_message: cmtp_send_message,
++ procinfo: cmtp_procinfo,
++ ctr_read_proc: cmtp_ctr_read_proc,
++
++ driver_read_proc: 0,
++ add_card: 0,
++};
++
++
++int cmtp_attach_device(struct cmtp_session *session)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[4];
++
++ BT_DBG("session %p", session);
++
++ capimsg_setu32(buf, 0, 0);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (session->ncontroller)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
++
++ if (!timeo)
++ return -ETIMEDOUT;
++
++ if (!session->ncontroller)
++ return -ENODEV;
++
++
++ if (session->ncontroller > 1)
++ BT_INFO("Setting up only CAPI controller 1");
++
++ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
++ BT_ERR("Can't attach new controller");
++ return -EBUSY;
++ }
++
++ session->num = session->ctrl->cnr;
++
++ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
++
++ capimsg_setu32(buf, 0, 1);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_VERSION, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ return 0;
++}
++
++void cmtp_detach_device(struct cmtp_session *session)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++
++ BT_DBG("session %p ctrl %p", session, ctrl);
++
++ if (!ctrl)
++ return;
++
++ ctrl->reseted(ctrl);
++
++ di->detach_ctr(ctrl);
++}
++
++int cmtp_init_capi(void)
++{
++ if (!(di = attach_capi_driver(&cmtp_driver))) {
++ BT_ERR("Can't attach CAPI driver");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_capi(void)
++{
++ detach_capi_driver(&cmtp_driver);
++}
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/cmtp.h
+@@ -0,0 +1,138 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __CMTP_H
++#define __CMTP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#define BTNAMSIZ 18
++
++/* CMTP ioctl defines */
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define CMTP_LOOPBACK 0
++
++struct cmtp_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++};
++
++struct cmtp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct cmtp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ int num;
++};
++
++struct cmtp_connlist_req {
++ __u32 cnum;
++ struct cmtp_conninfo *ci;
++};
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
++int cmtp_del_connection(struct cmtp_conndel_req *req);
++int cmtp_get_connlist(struct cmtp_connlist_req *req);
++int cmtp_get_conninfo(struct cmtp_conninfo *ci);
++
++/* CMTP session defines */
++#define CMTP_INTEROP_TIMEOUT (HZ * 5)
++#define CMTP_INITIAL_MSGNUM 0xff00
++
++struct cmtp_session {
++ struct list_head list;
++
++ struct socket *sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++
++ uint mtu;
++
++ char name[BTNAMSIZ];
++
++ atomic_t terminate;
++
++ wait_queue_head_t wait;
++
++ int ncontroller;
++ int num;
++ struct capi_ctr *ctrl;
++
++ struct list_head applications;
++
++ unsigned long blockids;
++ int msgnum;
++
++ struct sk_buff_head transmit;
++
++ struct sk_buff *reassembly[16];
++};
++
++struct cmtp_application {
++ struct list_head list;
++
++ unsigned long state;
++ int err;
++
++ __u16 appl;
++ __u16 mapping;
++
++ __u16 msgnum;
++};
++
++struct cmtp_scb {
++ int id;
++ int data;
++};
++
++int cmtp_attach_device(struct cmtp_session *session);
++void cmtp_detach_device(struct cmtp_session *session);
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++
++static inline void cmtp_schedule(struct cmtp_session *session)
++{
++ struct sock *sk = session->sock->sk;
++
++ wake_up_interruptible(sk->sleep);
++}
++
++/* CMTP init defines */
++int cmtp_init_capi(void);
++int cmtp_init_sockets(void);
++void cmtp_cleanup_capi(void);
++void cmtp_cleanup_sockets(void);
++
++#endif /* __CMTP_H */
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/core.c
+@@ -0,0 +1,515 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(cmtp_session_sem);
++static LIST_HEAD(cmtp_session_list);
++
++static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
++{
++ struct cmtp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &cmtp_session_list) {
++ session = list_entry(p, struct cmtp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __cmtp_link_session(struct cmtp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &cmtp_session_list);
++}
++
++static void __cmtp_unlink_session(struct cmtp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->num = session->num;
++}
++
++
++static inline int cmtp_alloc_block_id(struct cmtp_session *session)
++{
++ int i, id = -1;
++
++ for (i = 0; i < 16; i++)
++ if (!test_and_set_bit(i, &session->blockids)) {
++ id = i;
++ break;
++ }
++
++ return id;
++}
++
++static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
++{
++ clear_bit(id, &session->blockids);
++}
++
++static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
++{
++ struct sk_buff *skb = session->reassembly[id], *nskb;
++ int size;
++
++ BT_DBG("session %p buf %p count %d", session, buf, count);
++
++ size = (skb) ? skb->len + count : count;
++
++ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for CAPI message");
++ return;
++ }
++
++ if (skb && (skb->len > 0))
++ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
++
++ memcpy(skb_put(nskb, count), buf, count);
++
++ session->reassembly[id] = nskb;
++
++ if (skb)
++ kfree_skb(skb);
++}
++
++static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr, hdrlen, id;
++ __u16 len;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ while (skb->len > 0) {
++ hdr = skb->data[0];
++
++ switch (hdr & 0xc0) {
++ case 0x40:
++ hdrlen = 2;
++ len = skb->data[1];
++ break;
++ case 0x80:
++ hdrlen = 3;
++ len = skb->data[1] | (skb->data[2] << 8);
++ break;
++ default:
++ hdrlen = 1;
++ len = 0;
++ break;
++ }
++
++ id = (hdr & 0x3c) >> 2;
++
++ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
++
++ if (hdrlen + len > skb->len) {
++ BT_ERR("Wrong size or header information in CMTP frame");
++ break;
++ }
++
++ if (len == 0) {
++ skb_pull(skb, hdrlen);
++ continue;
++ }
++
++ switch (hdr & 0x03) {
++ case 0x00:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ cmtp_recv_capimsg(session, session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ case 0x01:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ break;
++ default:
++ if (session->reassembly[id] != NULL)
++ kfree_skb(session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ }
++
++ skb_pull(skb, hdrlen + len);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
++{
++ struct socket *sock = session->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
++
++ BT_DBG("session %p data %p len %d", session, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
++
++static int cmtp_process_transmit(struct cmtp_session *session)
++{
++ struct sk_buff *skb, *nskb;
++ unsigned char *hdr;
++ unsigned int size, tail;
++
++ BT_DBG("session %p", session);
++
++ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ while ((skb = skb_dequeue(&session->transmit))) {
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ if ((tail = (session->mtu - nskb->len)) < 5) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ tail = session->mtu;
++ }
++
++ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
++
++ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
++ skb_queue_head(&session->transmit, skb);
++ break;
++ }
++
++ if (size < 256) {
++ hdr = skb_put(nskb, 2);
++ hdr[0] = 0x40
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size;
++ } else {
++ hdr = skb_put(nskb, 3);
++ hdr[0] = 0x80
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size & 0xff;
++ hdr[2] = size >> 8;
++ }
++
++ memcpy(skb_put(nskb, size), skb->data, size);
++ skb_pull(skb, size);
++
++ if (skb->len > 0) {
++ skb_queue_head(&session->transmit, skb);
++ } else {
++ cmtp_free_block_id(session, scb->id);
++ if (scb->data) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ }
++ kfree_skb(skb);
++ }
++ }
++
++ cmtp_send_frame(session, nskb->data, nskb->len);
++
++ kfree_skb(nskb);
++
++ return skb_queue_len(&session->transmit);
++}
++
++static int cmtp_session(void *arg)
++{
++ struct cmtp_session *session = arg;
++ struct sock *sk = session->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("session %p", session);
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ cmtp_recv_frame(session, skb);
++ }
++
++ cmtp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ down_write(&cmtp_session_sem);
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK)))
++ cmtp_detach_device(session);
++
++ fput(session->sock->file);
++
++ __cmtp_unlink_session(session);
++
++ up_write(&cmtp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
++{
++ struct cmtp_session *session, *s;
++ bdaddr_t src, dst;
++ int i, err;
++
++ BT_DBG("");
++
++ baswap(&src, &bluez_pi(sock->sk)->src);
++ baswap(&dst, &bluez_pi(sock->sk)->dst);
++
++ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct cmtp_session));
++
++ down_write(&cmtp_session_sem);
++
++ s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
++
++ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
++
++ BT_DBG("mtu %d", session->mtu);
++
++ sprintf(session->name, "%s", batostr(&dst));
++
++ session->sock = sock;
++ session->state = BT_CONFIG;
++
++ init_waitqueue_head(&session->wait);
++
++ session->ctrl = NULL;
++ session->msgnum = CMTP_INITIAL_MSGNUM;
++
++ INIT_LIST_HEAD(&session->applications);
++
++ skb_queue_head_init(&session->transmit);
++
++ for (i = 0; i < 16; i++)
++ session->reassembly[i] = NULL;
++
++ session->flags = req->flags;
++
++ __cmtp_link_session(session);
++
++ err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
++ err = cmtp_attach_device(session);
++ if (err < 0)
++ goto detach;
++ }
++
++ up_write(&cmtp_session_sem);
++ return 0;
++
++detach:
++ cmtp_detach_device(session);
++
++unlink:
++ __cmtp_unlink_session(session);
++
++failed:
++ up_write(&cmtp_session_sem);
++ kfree(session);
++ return err;
++}
++
++int cmtp_del_connection(struct cmtp_conndel_req *req)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&req->bdaddr);
++ if (session) {
++ /* Flush the transmit queue */
++ skb_queue_purge(&session->transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++ } else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_connlist(struct cmtp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ list_for_each(p, &cmtp_session_list) {
++ struct cmtp_session *session;
++ struct cmtp_conninfo ci;
++
++ session = list_entry(p, struct cmtp_session, list);
++
++ __cmtp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_conninfo(struct cmtp_conninfo *ci)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&ci->bdaddr);
++ if (session)
++ __cmtp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++
++int __init init_cmtp(void)
++{
++ l2cap_load();
++
++ cmtp_init_capi();
++ cmtp_init_sockets();
++
++ BT_INFO("BlueZ CMTP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++void __exit exit_cmtp(void)
++{
++ cmtp_cleanup_sockets();
++ cmtp_cleanup_capi();
++}
++
++module_init(init_cmtp);
++module_exit(exit_cmtp);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/sock.c
+@@ -0,0 +1,208 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int cmtp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct cmtp_connadd_req ca;
++ struct cmtp_conndel_req cd;
++ struct cmtp_connlist_req cl;
++ struct cmtp_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case CMTPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = cmtp_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case CMTPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return cmtp_del_connection(&cd);
++
++ case CMTPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = cmtp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case CMTPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = cmtp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops cmtp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: cmtp_sock_release,
++ ioctl: cmtp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &cmtp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family cmtp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: cmtp_sock_create
++};
++
++int cmtp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
++ BT_ERR("Can't register CMTP socket layer (%d)", err);
++ return err;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
++ BT_ERR("Can't unregister CMTP socket layer (%d)", err);
++
++ return;
++}
+--- linux-2.4.21/net/bluetooth/hci_core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_core.c
+@@ -218,6 +218,10 @@
+
+ /* Mandatory initialization */
+
++ /* Reset */
++ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
++
+ /* Read Local Supported Features */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+@@ -395,7 +399,7 @@
+ {
+ struct hci_inquiry_req ir;
+ struct hci_dev *hdev;
+- int err = 0, do_inquiry = 0;
++ int err = 0, do_inquiry = 0, max_rsp;
+ long timeo;
+ __u8 *buf, *ptr;
+
+@@ -408,6 +412,7 @@
+
+ hci_dev_lock_bh(hdev);
+ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
++ inquiry_cache_empty(hdev) ||
+ ir.flags & IREQ_CACHE_FLUSH) {
+ inquiry_cache_flush(hdev);
+ do_inquiry = 1;
+@@ -418,16 +423,19 @@
+ if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+ goto done;
+
++ /* for unlimited number of responses we will use buffer with 255 entries */
++ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;
++
+ /* cache_dump can't sleep. Therefore we allocate temp buffer and then
+ * copy it to the user space.
+ */
+- if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
++ if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ hci_dev_lock_bh(hdev);
+- ir.num_rsp = inquiry_cache_dump(hdev, ir.num_rsp, buf);
++ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
+ hci_dev_unlock_bh(hdev);
+
+ BT_DBG("num_rsp %d", ir.num_rsp);
+@@ -708,22 +716,20 @@
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
+
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+ return -EINVAL;
+-
+- size = dev_num * sizeof(*dr) + sizeof(*dl);
+
+- if (verify_area(VERIFY_WRITE, (void *) arg, size))
+- return -EFAULT;
++ size = sizeof(*dl) + dev_num * sizeof(*dr);
+
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
++
+ dr = dl->dev_req;
+
+ read_lock_bh(&hdev_list_lock);
+@@ -738,12 +744,12 @@
+ read_unlock_bh(&hdev_list_lock);
+
+ dl->dev_num = n;
+- size = n * sizeof(*dr) + sizeof(*dl);
++ size = sizeof(*dl) + n * sizeof(*dr);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
+
+- return 0;
++ return err ? -EFAULT : 0;
+ }
+
+ int hci_get_dev_info(unsigned long arg)
+--- linux-2.4.21/net/bluetooth/hci_event.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_event.c
+@@ -62,9 +62,22 @@
+ /* Command Complete OGF LINK_CTL */
+ static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+ {
++ __u8 status;
++
+ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+ switch (ocf) {
++ case OCF_INQUIRY_CANCEL:
++ status = *((__u8 *) skb->data);
++
++ if (status) {
++ BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status);
++ } else {
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
+ default:
+ BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+ break;
+@@ -307,7 +320,7 @@
+ status, batostr(&cc->bdaddr), conn);
+
+ if (status) {
+- if (conn) {
++ if (conn && conn->state == BT_CONNECT) {
+ conn->state = BT_CLOSED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_del(conn);
+@@ -439,6 +452,29 @@
+ hci_dev_unlock(hdev);
+ }
+
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--) {
++ inquiry_info tmp;
++ bacpy(&tmp.bdaddr, &info->bdaddr);
++ tmp.pscan_rep_mode = info->pscan_rep_mode;
++ tmp.pscan_period_mode = info->pscan_period_mode;
++ tmp.pscan_mode = 0x00;
++ memcpy(tmp.dev_class, &info->dev_class, 3);
++ tmp.clock_offset = info->clock_offset;
++ info++;
++ inquiry_cache_update(hdev, &tmp);
++ }
++ hci_dev_unlock(hdev);
++}
++
+ /* Connect Request */
+ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+@@ -735,6 +771,10 @@
+ hci_inquiry_result_evt(hdev, skb);
+ break;
+
++ case EVT_INQUIRY_RESULT_WITH_RSSI:
++ hci_inquiry_result_with_rssi_evt(hdev, skb);
++ break;
++
+ case EVT_CONN_REQUEST:
+ hci_conn_request_evt(hdev, skb);
+ break;
+--- linux-2.4.21/net/bluetooth/hci_sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_sock.c
+@@ -66,20 +66,20 @@
+ /* Packet types */
+ 0x10,
+ /* Events */
+- { 0xd9fe, 0x0 },
++ { 0x1000d9fe, 0x0000300c },
+ /* Commands */
+ {
+ { 0x0 },
+ /* OGF_LINK_CTL */
+- { 0x2a000002, 0x0, 0x0, 0x0 },
++ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
+ /* OGF_LINK_POLICY */
+- { 0x1200, 0x0, 0x0, 0x0 },
++ { 0x00005200, 0x00000000, 0x0000, 0x00 },
+ /* OGF_HOST_CTL */
+- { 0x80100000, 0x2a, 0x0, 0x0 },
++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
+ /* OGF_INFO_PARAM */
+- { 0x22a, 0x0, 0x0, 0x0 },
++ { 0x000002be, 0x00000000, 0x0000, 0x00 },
+ /* OGF_STATUS_PARAM */
+- { 0x2e, 0x0, 0x0, 0x0 }
++ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
+ }
+ };
+
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Config.in
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
++
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
++
++O_TARGET := hidp.o
++
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/core.c
+@@ -0,0 +1,655 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <linux/input.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(hidp_session_sem);
++static LIST_HEAD(hidp_session_list);
++
++static unsigned char hidp_keycode[256] = {
++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
++ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
++ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
++ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
++ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
++ 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
++ 150,158,159,128,136,177,178,176,142,152,173,140
++};
++
++static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
++{
++ struct hidp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &hidp_session_list) {
++ session = list_entry(p, struct hidp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __hidp_link_session(struct hidp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &hidp_session_list);
++}
++
++static void __hidp_unlink_session(struct hidp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->vendor = 0x0000;
++ ci->product = 0x0000;
++ ci->version = 0x0000;
++ memset(ci->name, 0, 128);
++
++ if (session->input) {
++ ci->vendor = session->input->idvendor;
++ ci->product = session->input->idproduct;
++ ci->version = session->input->idversion;
++ if (session->input->name)
++ strncpy(ci->name, session->input->name, 128);
++ else
++ strncpy(ci->name, "HID Boot Device", 128);
++ }
++}
++
++static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct hidp_session *session = dev->private;
++ struct sk_buff *skb;
++ unsigned char newleds;
++
++ BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
++
++ if (type != EV_LED)
++ return -1;
++
++ newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
++ (!!test_bit(LED_COMPOSE, dev->led) << 3) |
++ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
++ (!!test_bit(LED_CAPSL, dev->led) << 1) |
++ (!!test_bit(LED_NUML, dev->led));
++
++ if (session->leds == newleds)
++ return 0;
++
++ session->leds = newleds;
++
++ if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ *skb_put(skb, 1) = 0xa2;
++ *skb_put(skb, 1) = 0x01;
++ *skb_put(skb, 1) = newleds;
++
++ skb_queue_tail(&session->intr_transmit, skb);
++
++ hidp_schedule(session);
++
++ return 0;
++}
++
++static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
++{
++ struct input_dev *dev = session->input;
++ unsigned char *keys = session->keys;
++ unsigned char *udata = skb->data + 1;
++ signed char *sdata = skb->data + 1;
++ int i, size = skb->len - 1;
++
++ switch (skb->data[0]) {
++ case 0x01: /* Keyboard report */
++ for (i = 0; i < 8; i++)
++ input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
++
++ for (i = 2; i < 8; i++) {
++ if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
++ if (hidp_keycode[keys[i]])
++ input_report_key(dev, hidp_keycode[keys[i]], 0);
++ else
++ BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
++ }
++
++ if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
++ if (hidp_keycode[udata[i]])
++ input_report_key(dev, hidp_keycode[udata[i]], 1);
++ else
++ BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
++ }
++ }
++
++ memcpy(keys, udata, 8);
++ break;
++
++ case 0x02: /* Mouse report */
++ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
++ input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
++ input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
++ input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
++ input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
++
++ input_report_rel(dev, REL_X, sdata[1]);
++ input_report_rel(dev, REL_Y, sdata[2]);
++
++ if (size > 3)
++ input_report_rel(dev, REL_WHEEL, sdata[3]);
++ break;
++ }
++
++ input_event(dev, EV_RST, 0, 0);
++}
++
++static void hidp_idle_timeout(unsigned long arg)
++{
++ struct hidp_session *session = (struct hidp_session *) arg;
++
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++}
++
++static inline void hidp_set_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ mod_timer(&session->timer, jiffies + HZ * session->idle_to);
++}
++
++static inline void hidp_del_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ del_timer(&session->timer);
++}
++
++static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for message");
++ return;
++ }
++
++ *skb_put(skb, 1) = hdr;
++
++ skb_queue_tail(&session->ctrl_transmit, skb);
++
++ hidp_schedule(session);
++}
++
++static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ hdr = skb->data[0];
++ skb_pull(skb, 1);
++
++ if (hdr == 0xa1) {
++ hidp_set_timer(session);
++
++ if (session->input)
++ hidp_input_report(session, skb);
++ } else {
++ BT_DBG("Unsupported protocol header 0x%02x", hdr);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
++{
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++
++ BT_DBG("sock %p data %p len %d", sock, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ return sock_sendmsg(sock, &msg, len);
++}
++
++static int hidp_process_transmit(struct hidp_session *session)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
++ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->ctrl_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ while ((skb = skb_dequeue(&session->intr_transmit))) {
++ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->intr_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ return skb_queue_len(&session->ctrl_transmit) +
++ skb_queue_len(&session->intr_transmit);
++}
++
++static int hidp_session(void *arg)
++{
++ struct hidp_session *session = arg;
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++ struct sk_buff *skb;
++ int vendor = 0x0000, product = 0x0000;
++ wait_queue_t ctrl_wait, intr_wait;
++ unsigned long timeo = HZ;
++
++ BT_DBG("session %p", session);
++
++ if (session->input) {
++ vendor = session->input->idvendor;
++ product = session->input->idproduct;
++ }
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&ctrl_wait, current);
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ hidp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++
++ down_write(&hidp_session_sem);
++
++ hidp_del_timer(session);
++
++ if (intr_sk->state != BT_CONNECTED) {
++ init_waitqueue_entry(&ctrl_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ while (timeo && ctrl_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ timeo = HZ;
++ }
++
++ fput(session->ctrl_sock->file);
++
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (timeo && intr_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++
++ fput(session->intr_sock->file);
++
++ __hidp_unlink_session(session);
++
++ if (session->input) {
++ input_unregister_device(session->input);
++ kfree(session->input);
++ }
++
++ up_write(&hidp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
++{
++ struct input_dev *input = session->input;
++ int i;
++
++ input->private = session;
++
++ input->idbus = BUS_BLUETOOTH;
++ input->idvendor = req->vendor;
++ input->idproduct = req->product;
++ input->idversion = req->version;
++
++ if (req->subclass & 0x40) {
++ set_bit(EV_KEY, input->evbit);
++ set_bit(EV_LED, input->evbit);
++ set_bit(EV_REP, input->evbit);
++
++ set_bit(LED_NUML, input->ledbit);
++ set_bit(LED_CAPSL, input->ledbit);
++ set_bit(LED_SCROLLL, input->ledbit);
++ set_bit(LED_COMPOSE, input->ledbit);
++ set_bit(LED_KANA, input->ledbit);
++
++ for (i = 0; i < sizeof(hidp_keycode); i++)
++ set_bit(hidp_keycode[i], input->keybit);
++ clear_bit(0, input->keybit);
++ }
++
++ if (req->subclass & 0x80) {
++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
++ input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
++ input->relbit[0] |= BIT(REL_WHEEL);
++ }
++
++ input->event = hidp_input_event;
++
++ input_register_device(input);
++}
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
++{
++ struct hidp_session *session, *s;
++ int err;
++
++ BT_DBG("");
++
++ if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
++ bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
++ return -ENOTUNIQ;
++
++ session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct hidp_session));
++
++ session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!session->input) {
++ kfree(session);
++ return -ENOMEM;
++ }
++ memset(session->input, 0, sizeof(struct input_dev));
++
++ down_write(&hidp_session_sem);
++
++ s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
++
++ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
++ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
++
++ BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
++
++ session->ctrl_sock = ctrl_sock;
++ session->intr_sock = intr_sock;
++ session->state = BT_CONNECTED;
++
++ init_timer(&session->timer);
++
++ session->timer.function = hidp_idle_timeout;
++ session->timer.data = (unsigned long) session;
++
++ skb_queue_head_init(&session->ctrl_transmit);
++ skb_queue_head_init(&session->intr_transmit);
++
++ session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
++ session->idle_to = req->idle_to;
++
++ if (session->input)
++ hidp_setup_input(session, req);
++
++ __hidp_link_session(session);
++
++ hidp_set_timer(session);
++
++ err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (session->input) {
++ hidp_send_message(session, 0x70);
++ session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
++
++ session->leds = 0xff;
++ hidp_input_event(session->input, EV_LED, 0, 0);
++ }
++
++ up_write(&hidp_session_sem);
++ return 0;
++
++unlink:
++ hidp_del_timer(session);
++
++ __hidp_unlink_session(session);
++
++ if (session->input)
++ input_unregister_device(session->input);
++
++failed:
++ up_write(&hidp_session_sem);
++
++ if (session->input)
++ kfree(session->input);
++
++ kfree(session);
++ return err;
++}
++
++int hidp_del_connection(struct hidp_conndel_req *req)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&req->bdaddr);
++ if (session) {
++ if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
++ hidp_send_message(session, 0x15);
++ } else {
++ /* Flush the transmit queues */
++ skb_queue_purge(&session->ctrl_transmit);
++ skb_queue_purge(&session->intr_transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++ }
++ } else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_connlist(struct hidp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ list_for_each(p, &hidp_session_list) {
++ struct hidp_session *session;
++ struct hidp_conninfo ci;
++
++ session = list_entry(p, struct hidp_session, list);
++
++ __hidp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&ci->bdaddr);
++ if (session)
++ __hidp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++static int __init hidp_init(void)
++{
++ l2cap_load();
++
++ hidp_init_sockets();
++
++ BT_INFO("BlueZ HIDP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++static void __exit hidp_exit(void)
++{
++ hidp_cleanup_sockets();
++}
++
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/hidp.h
+@@ -0,0 +1,122 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __HIDP_H
++#define __HIDP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++/* HIDP ioctl defines */
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
++#define HIDP_VIRTUAL_CABLE_UNPLUG 0
++#define HIDP_BOOT_PROTOCOL_MODE 1
++#define HIDP_BLUETOOTH_VENDOR_ID 9
++
++struct hidp_connadd_req {
++ int ctrl_sock; // Connected control socket
++ int intr_sock; // Connteted interrupt socket
++ __u16 parser;
++ __u16 rd_size;
++ __u8 *rd_data;
++ __u8 country;
++ __u8 subclass;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ __u32 flags;
++ __u32 idle_to;
++ char name[128];
++};
++
++struct hidp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct hidp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ char name[128];
++};
++
++struct hidp_connlist_req {
++ __u32 cnum;
++ struct hidp_conninfo *ci;
++};
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
++int hidp_del_connection(struct hidp_conndel_req *req);
++int hidp_get_connlist(struct hidp_connlist_req *req);
++int hidp_get_conninfo(struct hidp_conninfo *ci);
++
++/* HIDP session defines */
++struct hidp_session {
++ struct list_head list;
++
++ struct socket *ctrl_sock;
++ struct socket *intr_sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++ unsigned long idle_to;
++
++ uint ctrl_mtu;
++ uint intr_mtu;
++
++ atomic_t terminate;
++
++ unsigned char keys[8];
++ unsigned char leds;
++
++ struct input_dev *input;
++
++ struct timer_list timer;
++
++ struct sk_buff_head ctrl_transmit;
++ struct sk_buff_head intr_transmit;
++};
++
++static inline void hidp_schedule(struct hidp_session *session)
++{
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++
++ wake_up_interruptible(ctrl_sk->sleep);
++ wake_up_interruptible(intr_sk->sleep);
++}
++
++/* HIDP init defines */
++extern int __init hidp_init_sockets(void);
++extern void __exit hidp_cleanup_sockets(void);
++
++#endif /* __HIDP_H */
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/sock.c
+@@ -0,0 +1,212 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int hidp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct hidp_connadd_req ca;
++ struct hidp_conndel_req cd;
++ struct hidp_connlist_req cl;
++ struct hidp_conninfo ci;
++ struct socket *csock;
++ struct socket *isock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case HIDPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ csock = sockfd_lookup(ca.ctrl_sock, &err);
++ if (!csock)
++ return err;
++
++ isock = sockfd_lookup(ca.intr_sock, &err);
++ if (!isock) {
++ fput(csock->file);
++ return err;
++ }
++
++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++ fput(csock->file);
++ fput(isock->file);
++ return -EBADFD;
++ }
++
++ err = hidp_add_connection(&ca, csock, isock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else {
++ fput(csock->file);
++ fput(isock->file);
++ }
++
++ return err;
++
++ case HIDPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return hidp_del_connection(&cd);
++
++ case HIDPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = hidp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case HIDPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = hidp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops hidp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: hidp_sock_release,
++ ioctl: hidp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &hidp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family hidp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: hidp_sock_create
++};
++
++int __init hidp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++ BT_ERR("Can't register HIDP socket layer (%d)", err);
++
++ return err;
++}
++
++void __exit hidp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++ BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+--- linux-2.4.21/net/bluetooth/l2cap.c~bluetooth
++++ linux-2.4.21/net/bluetooth/l2cap.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $
+ */
+-#define VERSION "2.1"
++#define VERSION "2.3"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -284,7 +284,7 @@
+ l2cap_disconn_req req;
+
+ sk->state = BT_DISCONN;
+- l2cap_sock_set_timer(sk, HZ * 5);
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+@@ -309,11 +309,9 @@
+ static void l2cap_sock_close(struct sock *sk)
+ {
+ l2cap_sock_clear_timer(sk);
+-
+ lock_sock(sk);
+ __l2cap_sock_close(sk, ECONNRESET);
+ release_sock(sk);
+-
+ l2cap_sock_kill(sk);
+ }
+
+@@ -527,7 +525,8 @@
+ goto done;
+
+ wait:
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -760,32 +759,39 @@
+ static int l2cap_sock_shutdown(struct socket *sock, int how)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk) return 0;
+
+- l2cap_sock_clear_timer(sk);
+-
+ lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- __l2cap_sock_close(sk, ECONNRESET);
+- release_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ l2cap_sock_clear_timer(sk);
++ __l2cap_sock_close(sk, 0);
+
+- return 0;
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
+ }
+
+ static int l2cap_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk) return 0;
+
++ err = l2cap_sock_shutdown(sock, 2);
++
+ sock_orphan(sk);
+- l2cap_sock_close(sk);
+- return 0;
++ l2cap_sock_kill(sk);
++ return err;
+ }
+
+ /* --------- L2CAP channels --------- */
+@@ -917,10 +923,12 @@
+ hci_conn_put(conn->hcon);
+ }
+
+- sk->state = BT_CLOSED;
+- sk->err = err;
++ sk->state = BT_CLOSED;
+ sk->zapped = 1;
+
++ if (err)
++ sk->err = err;
++
+ if (parent)
+ parent->data_ready(parent, 0);
+ else
+@@ -956,6 +964,22 @@
+ read_unlock(&l->lock);
+ }
+
++/* Notify sockets that we cannot guaranty reliability anymore */
++static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
++ sk->err = err;
++ }
++ read_unlock(&l->lock);
++}
++
+ static void l2cap_chan_ready(struct sock *sk)
+ {
+ struct sock *parent = bluez_pi(sk)->parent;
+@@ -1320,15 +1344,18 @@
+ {
+ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+ void *ptr = rsp->data;
++ u16 flags = 0;
+
+ BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
+
+ if (result)
+ *result = l2cap_conf_output(sk, &ptr);
++ else
++ flags |= 0x0001;
+
+ rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp->result = __cpu_to_le16(result ? *result : 0);
+- rsp->flags = __cpu_to_le16(0);
++ rsp->flags = __cpu_to_le16(flags);
+
+ return ptr - data;
+ }
+@@ -1441,7 +1468,7 @@
+ case L2CAP_CR_SUCCESS:
+ sk->state = BT_CONFIG;
+ l2cap_pi(sk)->dcid = dcid;
+- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+
+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+ break;
+@@ -1476,7 +1503,7 @@
+
+ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+
+- if (flags & 0x01) {
++ if (flags & 0x0001) {
+ /* Incomplete config. Send empty response. */
+ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+ goto unlock;
+@@ -1489,12 +1516,12 @@
+ goto unlock;
+
+ /* Output config done */
+- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
+ sk->state = BT_CONNECTED;
+ l2cap_chan_ready(sk);
+- } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
++ } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
+ char req[64];
+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+ }
+@@ -1520,18 +1547,34 @@
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return -ENOENT;
+
+- if (result) {
+- l2cap_disconn_req req;
++ switch (result) {
++ case L2CAP_CONF_SUCCESS:
++ break;
+
+- /* They didn't like our options. Well... we do not negotiate.
+- * Close channel.
+- */
++ case L2CAP_CONF_UNACCEPT:
++ if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
++ char req[128];
++ /*
++ It does not make sense to adjust L2CAP parameters
++ that are currently defined in the spec. We simply
++ resend config request that we sent earlier. It is
++ stupid :) but it helps qualification testing
++ which expects at least some response from us.
++ */
++ l2cap_send_req(conn, L2CAP_CONF_REQ,
++ l2cap_build_conf_req(sk, req), req);
++ goto done;
++ }
++ default:
+ sk->state = BT_DISCONN;
++ sk->err = ECONNRESET;
+ l2cap_sock_set_timer(sk, HZ * 5);
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ {
++ l2cap_disconn_req req;
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ }
+ goto done;
+ }
+
+@@ -1539,9 +1582,9 @@
+ goto done;
+
+ /* Input config done */
+- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ sk->state = BT_CONNECTED;
+ l2cap_chan_ready(sk);
+ }
+@@ -1592,13 +1635,42 @@
+
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return 0;
+- l2cap_chan_del(sk, ECONNABORTED);
++ l2cap_chan_del(sk, 0);
+ bh_unlock_sock(sk);
+
+ l2cap_sock_kill(sk);
+ return 0;
+ }
+
++static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_req *req = (l2cap_info_req *) data;
++ l2cap_info_rsp rsp;
++ u16 type;
++
++ type = __le16_to_cpu(req->type);
++
++ BT_DBG("type 0x%4.4x", type);
++
++ rsp.type = __cpu_to_le16(type);
++ rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
++ return 0;
++}
++
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++ u16 type, result;
++
++ type = __le16_to_cpu(rsp->type);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
++
++ return 0;
++}
++
+ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+ {
+ __u8 *data = skb->data;
+@@ -1606,6 +1678,8 @@
+ l2cap_cmd_hdr cmd;
+ int err = 0;
+
++ l2cap_raw_recv(conn, skb);
++
+ while (len >= L2CAP_CMD_HDR_SIZE) {
+ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+ data += L2CAP_CMD_HDR_SIZE;
+@@ -1621,6 +1695,10 @@
+ }
+
+ switch (cmd.code) {
++ case L2CAP_COMMAND_REJ:
++ /* FIXME: We should process this */
++ break;
++
+ case L2CAP_CONN_REQ:
+ err = l2cap_connect_req(conn, &cmd, data);
+ break;
+@@ -1645,23 +1723,23 @@
+ err = l2cap_disconnect_rsp(conn, &cmd, data);
+ break;
+
+- case L2CAP_COMMAND_REJ:
+- /* FIXME: We should process this */
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+ case L2CAP_ECHO_REQ:
+ l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+ break;
+
+ case L2CAP_ECHO_RSP:
++ break;
++
+ case L2CAP_INFO_REQ:
++ err = l2cap_information_req(conn, &cmd, data);
++ break;
++
+ case L2CAP_INFO_RSP:
+- l2cap_raw_recv(conn, skb);
++ err = l2cap_information_rsp(conn, &cmd, data);
+ break;
+
+ default:
+- BT_ERR("Uknown signaling command 0x%2.2x", cmd.code);
++ BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+ err = -EINVAL;
+ break;
+ };
+@@ -1670,7 +1748,7 @@
+ l2cap_cmd_rej rej;
+ BT_DBG("error %d", err);
+
+- /* FIXME: Map err to a valid reason. */
++ /* FIXME: Map err to a valid reason */
+ rej.reason = __cpu_to_le16(0);
+ l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+ }
+@@ -1943,26 +2021,36 @@
+ kfree_skb(conn->rx_skb);
+ conn->rx_skb = NULL;
+ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
+ }
+
+ if (skb->len < 2) {
+- BT_ERR("Frame is too small (len %d)", skb->len);
++ BT_ERR("Frame is too short (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+ hdr = (l2cap_hdr *) skb->data;
+ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
+
+- BT_DBG("Start: total len %d, frag len %d", len, skb->len);
+-
+ if (len == skb->len) {
+ /* Complete frame received */
+ l2cap_recv_frame(conn, skb);
+ return 0;
+ }
+
+- /* Allocate skb for the complete frame (with header) */
+- if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
++ BT_DBG("Start: total len %d, frag len %d", len, skb->len);
++
++ if (skb->len > len) {
++ BT_ERR("Frame is too long (len %d, expected len %d)",
++ skb->len, len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ /* Allocate skb for the complete frame including header */
++ conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!conn->rx_skb)
+ goto drop;
+
+ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+@@ -1972,15 +2060,17 @@
+
+ if (!conn->rx_len) {
+ BT_ERR("Unexpected continuation frame (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+ if (skb->len > conn->rx_len) {
+- BT_ERR("Fragment is too large (len %d, expect %d)",
++ BT_ERR("Fragment is too long (len %d, expected %d)",
+ skb->len, conn->rx_len);
+ kfree_skb(conn->rx_skb);
+ conn->rx_skb = NULL;
+ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+@@ -2114,6 +2204,16 @@
+ BT_ERR("Can't unregister L2CAP protocol");
+ }
+
++void l2cap_load(void)
++{
++ /* Dummy function to trigger automatic L2CAP module loading by
++ other modules that use L2CAP sockets but do not use any other
++ symbols from it. */
++ return;
++}
++
++EXPORT_SYMBOL(l2cap_load);
++
+ module_init(l2cap_init);
+ module_exit(l2cap_cleanup);
+
+--- linux-2.4.21/net/bluetooth/rfcomm/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/core.c
+@@ -51,7 +51,7 @@
+ #include <net/bluetooth/l2cap.h>
+ #include <net/bluetooth/rfcomm.h>
+
+-#define VERSION "1.0"
++#define VERSION "1.1"
+
+ #ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+ #undef BT_DBG
+@@ -198,10 +198,11 @@
+
+ d->state = BT_OPEN;
+ d->flags = 0;
++ d->mscex = 0;
+ d->mtu = RFCOMM_DEFAULT_MTU;
+ d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
+
+- d->credits = RFCOMM_MAX_CREDITS;
++ d->cfc = RFCOMM_CFC_DISABLED;
+ d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+ }
+
+@@ -274,13 +275,13 @@
+ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+ {
+ struct rfcomm_session *s;
+- u8 dlci = __dlci(0, channel);
+ int err = 0;
++ u8 dlci;
+
+- BT_DBG("dlc %p state %ld %s %s channel %d dlci %d",
+- d, d->state, batostr(src), batostr(dst), channel, dlci);
++ BT_DBG("dlc %p state %ld %s %s channel %d",
++ d, d->state, batostr(src), batostr(dst), channel);
+
+- if (dlci < 1 || dlci > 62)
++ if (channel < 1 || channel > 30)
+ return -EINVAL;
+
+ if (d->state != BT_OPEN && d->state != BT_CLOSED)
+@@ -293,20 +294,23 @@
+ return err;
+ }
+
++ dlci = __dlci(!s->initiator, channel);
++
+ /* Check if DLCI already exists */
+ if (rfcomm_dlc_get(s, dlci))
+ return -EBUSY;
+
+ rfcomm_dlc_clear_state(d);
+
+- d->dlci = dlci;
+- d->addr = __addr(s->initiator, dlci);
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ d->priority = 7;
+
+- d->state = BT_CONFIG;
++ d->state = BT_CONFIG;
+ rfcomm_dlc_link(s, d);
+
+- d->mtu = s->mtu;
+- d->credits = s->credits;
++ d->mtu = s->mtu;
++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
+
+ if (s->state == BT_CONNECTED)
+ rfcomm_send_pn(s, 1, d);
+@@ -406,7 +410,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig |= RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -417,7 +421,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig &= ~RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -470,8 +474,8 @@
+ s->state = state;
+ s->sock = sock;
+
+- s->mtu = RFCOMM_DEFAULT_MTU;
+- s->credits = RFCOMM_MAX_CREDITS;
++ s->mtu = RFCOMM_DEFAULT_MTU;
++ s->cfc = RFCOMM_CFC_UNKNOWN;
+
+ list_add(&s->list, &session_list);
+
+@@ -707,7 +711,7 @@
+ hdr->len = __len8(sizeof(*mcc) + 1);
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
++ mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->len = __len8(1);
+
+ /* Type that we didn't like */
+@@ -733,16 +737,16 @@
+ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
++ mcc->type = __mcc_type(cr, RFCOMM_PN);
+ mcc->len = __len8(sizeof(*pn));
+
+ pn = (void *) ptr; ptr += sizeof(*pn);
+ pn->dlci = d->dlci;
+- pn->priority = 0;
++ pn->priority = d->priority;
+ pn->ack_timer = 0;
+ pn->max_retrans = 0;
+
+- if (d->credits) {
++ if (s->cfc) {
+ pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+ pn->credits = RFCOMM_DEFAULT_CREDITS;
+ } else {
+@@ -842,7 +846,51 @@
+
+ msc = (void *) ptr; ptr += sizeof(*msc);
+ msc->dlci = __addr(1, dlci);
+- msc->v24_sig = v24_sig;
++ msc->v24_sig = v24_sig | 0x01;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
++ mcc->len = __len8(0);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCON);
++ mcc->len = __len8(0);
+
+ *ptr = __fcs(buf); ptr++;
+
+@@ -1076,6 +1124,8 @@
+ d->state = BT_CONNECTED;
+ d->state_change(d, 0);
+ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+ } else {
+ rfcomm_send_dm(s, dlci);
+ }
+@@ -1085,29 +1135,23 @@
+
+ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
+ {
++ struct rfcomm_session *s = d->session;
++
+ BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
+ d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
+
+- if (cr) {
+- if (pn->flow_ctrl == 0xf0) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++ d->tx_credits = pn->credits;
+ } else {
+- if (pn->flow_ctrl == 0xe0) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ }
+
++ d->priority = pn->priority;
++
++ d->mtu = s->mtu = btohs(pn->mtu);
++
+ return 0;
+ }
+
+@@ -1133,7 +1177,7 @@
+ switch (d->state) {
+ case BT_CONFIG:
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_CONNECT;
+ rfcomm_send_sabm(s, d->dlci);
+ break;
+@@ -1144,7 +1188,7 @@
+
+ if (!cr)
+ return 0;
+-
++
+ /* PN request for non existing DLC.
+ * Assume incomming connection. */
+ if (rfcomm_connect_ind(s, channel, &d)) {
+@@ -1153,7 +1197,7 @@
+ rfcomm_dlc_link(s, d);
+
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_OPEN;
+ rfcomm_send_pn(s, 0, d);
+ } else {
+@@ -1198,6 +1242,14 @@
+ }
+ /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
+ no flow control lines, normal XON/XOFF chars */
++ if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
++ bit_rate = rpn->bit_rate;
++ if (bit_rate != RFCOMM_RPN_BR_115200) {
++ BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
++ bit_rate = RFCOMM_RPN_BR_115200;
++ rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
++ }
++ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
+ data_bits = __get_rpn_data_bits(rpn->line_settings);
+ if (data_bits != RFCOMM_RPN_DATA_8) {
+@@ -1223,23 +1275,26 @@
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+- if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+- BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
+- rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ flow_ctrl = rpn->flow_ctrl;
++ if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
++ BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+ rpn_mask ^= RFCOMM_RPN_PM_FLOW;
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
+- if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
+- BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
+- rpn->xon_char = RFCOMM_RPN_XON_CHAR;
++ xon_char = rpn->xon_char;
++ if (xon_char != RFCOMM_RPN_XON_CHAR) {
++ BT_DBG("RPN XON char mismatch 0x%x", xon_char);
++ xon_char = RFCOMM_RPN_XON_CHAR;
+ rpn_mask ^= RFCOMM_RPN_PM_XON;
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
+- if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+- BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
+- rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
++ xoff_char = rpn->xoff_char;
++ if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
++ BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
+ rpn_mask ^= RFCOMM_RPN_PM_XOFF;
+ }
+ }
+@@ -1280,12 +1335,12 @@
+
+ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+
+- if (!cr)
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d)
+ return 0;
+
+- d = rfcomm_dlc_get(s, dlci);
+- if (d) {
+- if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
++ if (cr) {
++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ else
+ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1296,7 +1351,11 @@
+ rfcomm_dlc_unlock(d);
+
+ rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
+- }
++
++ d->mscex |= RFCOMM_MSCEX_RX;
++ } else
++ d->mscex |= RFCOMM_MSCEX_TX;
++
+ return 0;
+ }
+
+@@ -1330,6 +1389,20 @@
+ rfcomm_recv_msc(s, cr, skb);
+ break;
+
++ case RFCOMM_FCOFF:
++ if (cr) {
++ set_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcoff(s, 0);
++ }
++ break;
++
++ case RFCOMM_FCON:
++ if (cr) {
++ clear_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcon(s, 0);
++ }
++ break;
++
+ case RFCOMM_TEST:
+ if (cr)
+ rfcomm_send_test(s, 0, skb->data, skb->len);
+@@ -1358,7 +1431,7 @@
+ goto drop;
+ }
+
+- if (pf && d->credits) {
++ if (pf && d->cfc) {
+ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+
+ d->tx_credits += credits;
+@@ -1463,20 +1536,20 @@
+ struct sk_buff *skb;
+ int err;
+
+- BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d",
+- d, d->state, d->credits, d->rx_credits, d->tx_credits);
++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
++ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
+
+ /* Send pending MSC */
+ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
+ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+
+- if (d->credits) {
++ if (d->cfc) {
+ /* CFC enabled.
+ * Give them some credits */
+ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
+- d->rx_credits <= (d->credits >> 2)) {
+- rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
+- d->rx_credits = d->credits;
++ d->rx_credits <= (d->cfc >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
++ d->rx_credits = d->cfc;
+ }
+ } else {
+ /* CFC disabled.
+@@ -1497,7 +1570,7 @@
+ d->tx_credits--;
+ }
+
+- if (d->credits && !d->tx_credits) {
++ if (d->cfc && !d->tx_credits) {
+ /* We're out of TX credits.
+ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1520,7 +1593,11 @@
+ continue;
+ }
+
+- if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
++ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
++ continue;
++
++ if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
++ d->mscex == RFCOMM_MSCEX_OK)
+ rfcomm_process_tx(d);
+ }
+ }
+@@ -1577,9 +1654,10 @@
+ nsock->sk->state_change = rfcomm_l2state_change;
+
+ s = rfcomm_session_add(nsock, BT_OPEN);
+- if (s)
++ if (s) {
+ rfcomm_session_hold(s);
+- else
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++ } else
+ sock_release(nsock);
+ }
+
+@@ -1815,6 +1893,8 @@
+ /* ---- Initialization ---- */
+ int __init rfcomm_init(void)
+ {
++ l2cap_load();
++
+ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+ rfcomm_init_sockets();
+--- linux-2.4.21/net/bluetooth/rfcomm/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/sock.c
+@@ -188,8 +188,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted dlcs */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ rfcomm_sock_close(sk);
++ rfcomm_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -211,15 +213,10 @@
+ sock_put(sk);
+ }
+
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void rfcomm_sock_close(struct sock *sk)
++static void __rfcomm_sock_close(struct sock *sk)
+ {
+ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+- lock_sock(sk);
+-
+ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+ switch (sk->state) {
+@@ -236,11 +233,17 @@
+ default:
+ sk->zapped = 1;
+ break;
+- };
++ }
++}
+
++/* Close socket.
++ * Must be called on unlocked socket.
++ */
++static void rfcomm_sock_close(struct sock *sk)
++{
++ lock_sock(sk);
++ __rfcomm_sock_close(sk);
+ release_sock(sk);
+-
+- rfcomm_sock_kill(sk);
+ }
+
+ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+@@ -374,7 +377,8 @@
+
+ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+ if (!err)
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ release_sock(sk);
+ return err;
+@@ -558,9 +562,6 @@
+ int target, err = 0, copied = 0;
+ long timeo;
+
+- if (sk->state != BT_CONNECTED)
+- return -EINVAL;
+-
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+@@ -635,23 +636,6 @@
+ return copied ? : err;
+ }
+
+-static int rfcomm_sock_shutdown(struct socket *sock, int how)
+-{
+- struct sock *sk = sock->sk;
+-
+- BT_DBG("sock %p, sk %p", sock, sk);
+-
+- if (!sk) return 0;
+-
+- lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- if (sk->state == BT_CONNECTED)
+- rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
+- release_sock(sk);
+-
+- return 0;
+-}
+-
+ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+ {
+ struct sock *sk = sock->sk;
+@@ -711,19 +695,42 @@
+ return err;
+ }
+
++static int rfcomm_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ __rfcomm_sock_close(sk);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
++}
++
+ static int rfcomm_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+- sock_orphan(sk);
+- rfcomm_sock_close(sk);
++ err = rfcomm_sock_shutdown(sock, 2);
+
+- return 0;
++ sock_orphan(sk);
++ rfcomm_sock_kill(sk);
++ return err;
+ }
+
+ /* ---- RFCOMM core layer callbacks ----
+--- linux-2.4.21/net/bluetooth/rfcomm/tty.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/tty.c
+@@ -109,6 +109,13 @@
+
+ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
+ {
++ /* The reason this isn't actually a race, as you no
++ doubt have a little voice screaming at you in your
++ head, is that the refcount should never actually
++ reach zero unless the device has already been taken
++ off the list, in rfcomm_dev_del(). And if that's not
++ true, we'll hit the BUG() in rfcomm_dev_destruct()
++ anyway. */
+ if (atomic_dec_and_test(&dev->refcnt))
+ rfcomm_dev_destruct(dev);
+ }
+@@ -132,10 +139,13 @@
+ struct rfcomm_dev *dev;
+
+ read_lock(&rfcomm_dev_lock);
++
+ dev = __rfcomm_dev_get(id);
++ if (dev)
++ rfcomm_dev_hold(dev);
++
+ read_unlock(&rfcomm_dev_lock);
+
+- if (dev) rfcomm_dev_hold(dev);
+ return dev;
+ }
+
+@@ -260,9 +270,9 @@
+ skb->destructor = rfcomm_wfree;
+ }
+
+-static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
+ {
+- if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
+ struct sk_buff *skb = alloc_skb(size, priority);
+ if (skb) {
+ rfcomm_set_owner_w(skb, dev);
+@@ -328,12 +338,14 @@
+
+ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
+
+- if (!capable(CAP_NET_ADMIN))
+- return -EPERM;
+-
+ if (!(dev = rfcomm_dev_get(req.dev_id)))
+ return -ENODEV;
+
++ if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
++ rfcomm_dev_put(dev);
++ return -EPERM;
++ }
++
+ if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+ rfcomm_dlc_close(dev->dlc, 0);
+
+@@ -347,7 +359,7 @@
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ u16 dev_num;
+
+ BT_DBG("");
+@@ -355,14 +367,11 @@
+ if (get_user(dev_num, (u16 *) arg))
+ return -EFAULT;
+
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
+ return -EINVAL;
+
+ size = sizeof(*dl) + dev_num * sizeof(*di);
+
+- if (verify_area(VERIFY_WRITE, (void *)arg, size))
+- return -EFAULT;
+-
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
+
+@@ -387,9 +396,10 @@
+ dl->dev_num = n;
+ size = sizeof(*dl) + n * sizeof(*di);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
+- return 0;
++
++ return err ? -EFAULT : 0;
+ }
+
+ static int rfcomm_get_dev_info(unsigned long arg)
+@@ -486,7 +496,8 @@
+ rfcomm_dev_del(dev);
+
+ /* We have to drop DLC lock here, otherwise
+- * rfcomm_dev_put() will dead lock if it's the last refference */
++ rfcomm_dev_put() will dead lock if it's
++ the last reference. */
+ rfcomm_dlc_unlock(dlc);
+ rfcomm_dev_put(dev);
+ rfcomm_dlc_lock(dlc);
+@@ -541,6 +552,10 @@
+
+ BT_DBG("tty %p id %d", tty, id);
+
++ /* We don't leak this refcount. For reasons which are not entirely
++ clear, the TTY layer will call our ->close() method even if the
++ open fails. We decrease the refcount there, and decreasing it
++ here too would cause breakage. */
+ dev = rfcomm_dev_get(id);
+ if (!dev)
+ return -ENODEV;
+@@ -627,9 +642,9 @@
+ size = min_t(uint, count, dlc->mtu);
+
+ if (from_user)
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
+ else
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
+
+ if (!skb)
+ break;
+@@ -653,6 +668,27 @@
+ return sent ? sent : err;
+ }
+
++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
++
++ BT_DBG("tty %p char %x", tty, ch);
++
++ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
++
++ if (!skb)
++ return;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ *(char *)skb_put(skb, 1) = ch;
++
++ if ((rfcomm_dlc_send(dlc, skb)) < 0)
++ kfree_skb(skb);
++}
++
+ static int rfcomm_tty_write_room(struct tty_struct *tty)
+ {
+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+@@ -879,6 +915,7 @@
+
+ open: rfcomm_tty_open,
+ close: rfcomm_tty_close,
++ put_char: rfcomm_tty_put_char,
+ write: rfcomm_tty_write,
+ write_room: rfcomm_tty_write_room,
+ chars_in_buffer: rfcomm_tty_chars_in_buffer,
+--- linux-2.4.21/net/bluetooth/sco.c~bluetooth
++++ linux-2.4.21/net/bluetooth/sco.c
+@@ -332,8 +332,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted channels */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ sco_sock_close(sk);
++ sco_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -388,8 +390,6 @@
+ };
+
+ release_sock(sk);
+-
+- sco_sock_kill(sk);
+ }
+
+ static void sco_sock_init(struct sock *sk, struct sock *parent)
+@@ -508,7 +508,8 @@
+ if ((err = sco_connect(sk)))
+ goto done;
+
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -712,16 +713,23 @@
+ static int sco_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+- sock_orphan(sk);
+ sco_sock_close(sk);
++ if (sk->linger) {
++ lock_sock(sk);
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ release_sock(sk);
++ }
+
+- return 0;
++ sock_orphan(sk);
++ sco_sock_kill(sk);
++ return err;
+ }
+
+ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+--- linux-2.4.21/net/bluetooth/syms.c~bluetooth
++++ linux-2.4.21/net/bluetooth/syms.c
+@@ -78,4 +78,4 @@
+ EXPORT_SYMBOL(bluez_sock_poll);
+ EXPORT_SYMBOL(bluez_accept_enqueue);
+ EXPORT_SYMBOL(bluez_accept_dequeue);
+-EXPORT_SYMBOL(bluez_sock_w4_connect);
++EXPORT_SYMBOL(bluez_sock_wait_state);
+--- 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
+@@ -160,6 +160,7 @@
+ EXPORT_SYMBOL(put_cmsg);
+ EXPORT_SYMBOL(sock_kmalloc);
+ EXPORT_SYMBOL(sock_kfree_s);
++EXPORT_SYMBOL(sockfd_lookup);
+
+ #ifdef CONFIG_FILTER
+ EXPORT_SYMBOL(sk_run_filter);
+@@ -601,6 +602,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 */